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
This commit is contained in:
Calinteodor 2022-06-21 17:16:38 +03:00 committed by GitHub
parent d0790736db
commit bb76090bce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 56 deletions

View File

@ -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 (
<KeyboardAvoidingView
behavior = { Platform.OS === 'ios' ? 'padding' : 'height' }
@ -88,6 +93,8 @@ const JitsiKeyboardAvoidingView = (
? iosVerticalOffset
: androidVerticalOffset
}
onResponderRelease = { onRelease }
onStartShouldSetResponder = { shouldSetResponse }
style = { style }>
{ children }
</KeyboardAvoidingView>

View File

@ -74,9 +74,9 @@ const JitsiScreen = ({
<SafeAreaView
edges = { safeAreaInsets }
style = { styles.safeArea }>
{ children }
{children}
</SafeAreaView>
{ footerComponent && footerComponent() }
{footerComponent && footerComponent()}
</JitsiKeyboardAvoidingView>
</View>
);

View File

@ -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<Props, State> {
*/
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this._onHardwareBackPress);
setPictureInPictureEnabled(true);
}
/**
@ -189,10 +199,18 @@ class Conference extends AbstractConference<Props, State> {
* @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<Props, State> {
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)
};

View File

@ -58,6 +58,7 @@ class LobbyScreen extends AbstractLobbyScreen<Props> {
return (
<JitsiScreen
safeAreaInsets = { [ 'left' ] }
style = { contentWrapperStyles }>
<BrandingImageBackground />
<View style = { largeVideoContainerStyles }>

View File

@ -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: {

View File

@ -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;
}
}

View File

@ -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 (
<HeaderNavigationButton
label = { t('dialog.close') }
onPress = { goBack } />
);
}
return (
<HeaderNavigationButton
onPress = { goBack }
src = { IconClose } />
);
}, []);
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 (
<HeaderNavigationButton
label = { t('dialog.close') }
onPress = { goBack } />
);
}
return (
<HeaderNavigationButton
onPress = { goBack }
src = { IconClose } />
);
}, []);
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', goBack);
return () => BackHandler.removeEventListener('hardwareBackPress', goBack)
}, [ ]);
useLayoutEffect(() => {
navigation.setOptions({
headerLeft
});
}, [ navigation ]);
return (
<JitsiScreen
safeAreaInsets = { [ 'right' ] }
safeAreaInsets = { [ 'left' ] }
style = { contentWrapperStyles }>
<BrandingImageBackground />
<View style = { largeVideoContainerStyles }>
@ -120,13 +135,15 @@ const Prejoin: ({ navigation }: Props) => JSX.Element = ({ navigation }: Props)
<TextInput
onChangeText = { onChangeDisplayName }
placeholder = { t('dialog.enterDisplayName') }
placeholderTextColor = { BaseTheme.palette.text03 }
style = { styles.field }
value = { displayName } />
<TouchableOpacity
disabled = { joinButtonDisabled }
onPress = { onJoin }
style = { [
styles.button,
styles.primaryButton
joinButtonStyles
] }>
<Text style = { styles.primaryButtonText }>
{ t('prejoin.joinMeeting') }

View File

@ -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'
}
};

View File

@ -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;
}
/**