From bb76090bced3a605f57263239e4d360d25b8b435 Mon Sep 17 00:00:00 2001 From: Calinteodor Date: Tue, 21 Jun 2022 17:16:38 +0300 Subject: [PATCH] feat(lobby/prejoin): updates * feat(base/modal) added keyboard dismiss functionality * feat(lobby) updated ui and start knocking if name is set * feat(prejoin) updated ui and hide input if name is not required * feat(prejoin) updated join button styles * feat(prejoin) removed extra empty space * feat(prejoin) updated disable join condition * feat(base/modal) moved keaboard dismiss functionality * feat(conference) updated auto knock condition * feat(prejoin) updated button styles and disabling condition * feat(lobby) updated styles * feat(lobby/prejoin) updated styles for buttons and inputs * feat(lobby/prejoin) updated contentContainer styles * feat(lobby/prejoin) created shouldEnableAutoKnock helper --- .../components/JitsiKeyboardAvoidingView.js | 9 ++- .../base/modal/components/JitsiScreen.js | 4 +- .../components/native/Conference.js | 26 +++++- .../lobby/components/native/LobbyScreen.js | 1 + .../lobby/components/native/styles.js | 20 +++-- react/features/mobile/navigation/functions.js | 24 +++++- .../prejoin/components/Prejoin.native.tsx | 81 +++++++++++-------- react/features/prejoin/components/styles.js | 19 +++-- react/features/prejoin/functions.js | 4 +- 9 files changed, 132 insertions(+), 56 deletions(-) diff --git a/react/features/base/modal/components/JitsiKeyboardAvoidingView.js b/react/features/base/modal/components/JitsiKeyboardAvoidingView.js index 740987642..9d996d414 100644 --- a/react/features/base/modal/components/JitsiKeyboardAvoidingView.js +++ b/react/features/base/modal/components/JitsiKeyboardAvoidingView.js @@ -1,8 +1,9 @@ // @flow import { getDefaultHeaderHeight } from '@react-navigation/elements'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { + Keyboard, KeyboardAvoidingView, Platform, StatusBar @@ -78,6 +79,10 @@ const JitsiKeyboardAvoidingView = ( const androidVerticalOffset = hasBottomTextInput ? deviceHeight + StatusBar.currentHeight : deviceHeight; + // Tells the view what to do with taps + const shouldSetResponse = useCallback(() => true); + const onRelease = useCallback(() => Keyboard.dismiss()); + return ( { children } diff --git a/react/features/base/modal/components/JitsiScreen.js b/react/features/base/modal/components/JitsiScreen.js index ff868fcab..2678aa820 100644 --- a/react/features/base/modal/components/JitsiScreen.js +++ b/react/features/base/modal/components/JitsiScreen.js @@ -74,9 +74,9 @@ const JitsiScreen = ({ - { children } + {children} - { footerComponent && footerComponent() } + {footerComponent && footerComponent()} ); diff --git a/react/features/conference/components/native/Conference.js b/react/features/conference/components/native/Conference.js index 18d5bd88f..99ed075cf 100644 --- a/react/features/conference/components/native/Conference.js +++ b/react/features/conference/components/native/Conference.js @@ -22,12 +22,13 @@ import { } from '../../../filmstrip'; import { CalleeInfoContainer } from '../../../invite'; import { LargeVideo } from '../../../large-video'; +import { startKnocking } from '../../../lobby/actions.any'; import { KnockingParticipantList } from '../../../lobby/components/native'; import { getIsLobbyVisible } from '../../../lobby/functions'; import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; +import { shouldEnableAutoKnock } from '../../../mobile/navigation/functions'; import { screen } from '../../../mobile/navigation/routes'; -import { setPictureInPictureEnabled } from '../../../mobile/picture-in-picture'; import { Captions } from '../../../subtitles'; import { setToolboxVisible } from '../../../toolbox/actions'; import { Toolbox } from '../../../toolbox/components/native'; @@ -100,6 +101,11 @@ type Props = AbstractProps & { */ _largeVideoParticipantId: string, + /** + * Local participant's display name. + */ + _localParticipantDisplayName: string, + /** * Whether Picture-in-Picture is enabled. */ @@ -116,6 +122,11 @@ type Props = AbstractProps & { */ _toolboxVisible: boolean, + /** + * Indicates if we should auto-knock. + */ + _shouldEnableAutoKnock: boolean, + /** * Indicates whether the lobby screen should be visible. */ @@ -180,7 +191,6 @@ class Conference extends AbstractConference { */ componentDidMount() { BackHandler.addEventListener('hardwareBackPress', this._onHardwareBackPress); - setPictureInPictureEnabled(true); } /** @@ -189,10 +199,18 @@ class Conference extends AbstractConference { * @inheritdoc */ componentDidUpdate(prevProps) { - const { _showLobby } = this.props; + const { + _shouldEnableAutoKnock, + _showLobby, + dispatch + } = this.props; if (!prevProps._showLobby && _showLobby) { navigate(screen.lobby.root); + + if (_shouldEnableAutoKnock) { + dispatch(startKnocking()); + } } if (prevProps._showLobby && !_showLobby) { @@ -213,7 +231,6 @@ class Conference extends AbstractConference { BackHandler.removeEventListener('hardwareBackPress', this._onHardwareBackPress); clearTimeout(this._expandedLabelTimeout.current); - setPictureInPictureEnabled(false); } /** @@ -535,6 +552,7 @@ function _mapStateToProps(state) { _largeVideoParticipantId: state['features/large-video'].participantId, _pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED), _reducedUI: reducedUI, + _shouldEnableAutoKnock: shouldEnableAutoKnock(state), _showLobby: getIsLobbyVisible(state), _toolboxVisible: isToolboxVisible(state) }; diff --git a/react/features/lobby/components/native/LobbyScreen.js b/react/features/lobby/components/native/LobbyScreen.js index 5c3737194..1afaf32dd 100644 --- a/react/features/lobby/components/native/LobbyScreen.js +++ b/react/features/lobby/components/native/LobbyScreen.js @@ -58,6 +58,7 @@ class LobbyScreen extends AbstractLobbyScreen { return ( diff --git a/react/features/lobby/components/native/styles.js b/react/features/lobby/components/native/styles.js index ba90f9ddf..11563b2e0 100644 --- a/react/features/lobby/components/native/styles.js +++ b/react/features/lobby/components/native/styles.js @@ -24,7 +24,8 @@ export default { marginHorizontal: BaseTheme.spacing[3], height: 24, width: 24 - } + }, + underlayColor: 'transparent' }, lobbyChatWrapper: { @@ -75,18 +76,20 @@ export default { }, contentContainer: { - alignItems: 'center', + alignSelf: 'center', display: 'flex', justifyContent: 'center', - minHeight: '50%' + minHeight: '50%', + paddingHorizontal: BaseTheme.spacing[3], + width: 400 }, contentContainerWide: { + alignItems: 'center', height: '100%', justifyContent: 'center', left: '50%', - marginHorizontal: BaseTheme.spacing[6], - marginVertical: BaseTheme.spacing[3], + paddingHorizontal: BaseTheme.spacing[3], position: 'absolute', width: '50%' }, @@ -132,7 +135,8 @@ export default { borderRadius: BaseTheme.shape.borderRadius, borderWidth: 2, height: BaseTheme.spacing[7], - marginHorizontal: BaseTheme.spacing[3], + marginTop: BaseTheme.spacing[3], + marginHorizontal: 12, padding: BaseTheme.spacing[2], textAlign: 'center' }, @@ -182,12 +186,12 @@ export default { primaryButton: { backgroundColor: BaseTheme.palette.action01, - marginTop: BaseTheme.spacing[4] + marginTop: BaseTheme.spacing[3] }, primaryButtonDisabled: { backgroundColor: BaseTheme.palette.action03Disabled, - marginTop: BaseTheme.spacing[4] + marginTop: BaseTheme.spacing[3] }, primaryButtonText: { diff --git a/react/features/mobile/navigation/functions.js b/react/features/mobile/navigation/functions.js index df002f447..d7c21ed45 100644 --- a/react/features/mobile/navigation/functions.js +++ b/react/features/mobile/navigation/functions.js @@ -3,12 +3,14 @@ import { useTranslation } from 'react-i18next'; import { Platform } from 'react-native'; import { useDispatch } from 'react-redux'; + import { appNavigate } from '../../app/actions'; import { getFeatureFlag, PREJOIN_PAGE_ENABLED } from '../../base/flags'; import { IconClose } from '../../base/icons'; +import { toState } from '../../base/redux'; import { cancelKnocking } from '../../lobby/actions.native'; import HeaderNavigationButton from './components/HeaderNavigationButton'; @@ -48,7 +50,7 @@ export function screenHeaderCloseButton(goBack: Function) { * {@code true}; otherwise, {@code false}. */ export function isPrejoinPageEnabled(stateful: Function | Object) { - return getFeatureFlag(stateful, PREJOIN_PAGE_ENABLED, true); + return getFeatureFlag(toState(stateful), PREJOIN_PAGE_ENABLED, true); } /** @@ -78,3 +80,23 @@ export function lobbyScreenHeaderCloseButton() { src = { IconClose } /> ); } + +/** + * Returns true if we should auto-knock in case prejoin is enabled for the room. + * + * @param {Function|Object} stateful - The redux state or {@link getState} + * function. + * @returns {boolean} + */ +export function shouldEnableAutoKnock(stateful: Function | Object) { + const state = toState(stateful); + const { displayName } = state['features/base/settings']; + + if (isPrejoinPageEnabled(state)) { + if (displayName) { + return true; + } + } else { + return false; + } +} diff --git a/react/features/prejoin/components/Prejoin.native.tsx b/react/features/prejoin/components/Prejoin.native.tsx index ae4f4c2de..f331d02ea 100644 --- a/react/features/prejoin/components/Prejoin.native.tsx +++ b/react/features/prejoin/components/Prejoin.native.tsx @@ -1,6 +1,13 @@ import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Text, View, TouchableOpacity, TextInput, Platform, BackHandler } from 'react-native'; +import { + BackHandler, + Text, + View, + TouchableOpacity, + TextInput, + Platform +} from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { appNavigate } from '../../app/actions.native'; @@ -12,6 +19,7 @@ import { getLocalParticipant } from '../../base/participants'; import { getFieldValue } from '../../base/react'; import { ASPECT_RATIO_NARROW } from '../../base/responsive-ui'; import { updateSettings } from '../../base/settings'; +import BaseTheme from '../../base/ui/components/BaseTheme.native'; import { BrandingImageBackground } from '../../dynamic-branding'; import { LargeVideo } from '../../large-video/components'; import HeaderNavigationButton from '../../mobile/navigation/components/HeaderNavigationButton'; @@ -19,6 +27,7 @@ import { navigateRoot } from '../../mobile/navigation/rootNavigationContainerRef import { screen } from '../../mobile/navigation/routes'; import AudioMuteButton from '../../toolbox/components/AudioMuteButton'; import VideoMuteButton from '../../toolbox/components/VideoMuteButton'; +import { isDisplayNameRequired } from '../functions'; import styles from './styles'; @@ -34,6 +43,7 @@ const Prejoin: ({ navigation }: Props) => JSX.Element = ({ navigation }: Props) (state: any) => state['features/base/responsive-ui']?.aspectRatio ); const localParticipant = useSelector(state => getLocalParticipant(state)); + const isDisplayNameMandatory = useSelector(state => isDisplayNameRequired(state)); const participantName = localParticipant?.name; const [ displayName, setDisplayName ] = useState(participantName || ''); @@ -58,9 +68,43 @@ const Prejoin: ({ navigation }: Props) => JSX.Element = ({ navigation }: Props) const goBack = useCallback(() => { dispatch(appNavigate(undefined)); + return true; }, [ dispatch ]); + const headerLeft = useCallback(() => { + if (Platform.OS === 'ios') { + return ( + + ); + } + + return ( + + ); + }, []); + + const joinButtonDisabled = !displayName && isDisplayNameMandatory; + const joinButtonStyles = joinButtonDisabled + ? styles.primaryButtonDisabled : styles.primaryButton; + + useEffect(() => { + BackHandler.addEventListener('hardwareBackPress', goBack); + + return () => BackHandler.removeEventListener('hardwareBackPress', goBack); + + }, [ ]); + + useLayoutEffect(() => { + navigation.setOptions({ + headerLeft + }); + }, [ navigation ]); + let contentWrapperStyles; let contentContainerStyles; let largeVideoContainerStyles; @@ -78,38 +122,9 @@ const Prejoin: ({ navigation }: Props) => JSX.Element = ({ navigation }: Props) toolboxContainerStyles = styles.toolboxContainerWide; } - const headerLeft = useCallback(() => { - if (Platform.OS === 'ios') { - return ( - - ); - } - - return ( - - ); - }, []); - - useEffect(() => { - BackHandler.addEventListener('hardwareBackPress', goBack); - - return () => BackHandler.removeEventListener('hardwareBackPress', goBack) - - }, [ ]); - - useLayoutEffect(() => { - navigation.setOptions({ - headerLeft - }); - }, [ navigation ]); - return ( @@ -120,13 +135,15 @@ const Prejoin: ({ navigation }: Props) => JSX.Element = ({ navigation }: Props) { t('prejoin.joinMeeting') } diff --git a/react/features/prejoin/components/styles.js b/react/features/prejoin/components/styles.js index 496462b0d..3e7b28ad8 100644 --- a/react/features/prejoin/components/styles.js +++ b/react/features/prejoin/components/styles.js @@ -23,6 +23,10 @@ export default { backgroundColor: BaseTheme.palette.action01 }, + primaryButtonDisabled: { + backgroundColor: BaseTheme.palette.action03Disabled, + marginTop: BaseTheme.spacing[4] + }, primaryButtonText: { ...btnText @@ -49,7 +53,8 @@ export default { marginHorizontal: BaseTheme.spacing[3], height: 24, width: 24 - } + }, + underlayColor: 'transparent' }, contentWrapper: { @@ -73,18 +78,20 @@ export default { }, contentContainer: { - alignItems: 'center', + alignSelf: 'center', display: 'flex', justifyContent: 'center', - minHeight: '50%' + minHeight: '50%', + paddingHorizontal: BaseTheme.spacing[3], + width: 400 }, contentContainerWide: { + alignItems: 'center', height: '100%', justifyContent: 'center', left: '50%', - marginHorizontal: BaseTheme.spacing[6], - marginVertical: BaseTheme.spacing[3], + paddingHorizontal: BaseTheme.spacing[3], position: 'absolute', width: '50%' }, @@ -115,7 +122,7 @@ export default { borderRadius: BaseTheme.shape.borderRadius, borderWidth: 2, height: BaseTheme.spacing[7], - marginTop: BaseTheme.spacing[2], + marginTop: BaseTheme.spacing[3], textAlign: 'center' } }; diff --git a/react/features/prejoin/functions.js b/react/features/prejoin/functions.js index 9b313b050..001f30d52 100644 --- a/react/features/prejoin/functions.js +++ b/react/features/prejoin/functions.js @@ -32,8 +32,8 @@ export function isDeviceStatusVisible(state: Object): boolean { * @returns {boolean} */ export function isDisplayNameRequired(state: Object): boolean { - return state['features/prejoin'].isDisplayNameRequired - || state['features/base/config'].requireDisplayName; + return state['features/prejoin']?.isDisplayNameRequired + || state['features/base/config']?.requireDisplayName; } /**