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:
parent
d0790736db
commit
bb76090bce
|
@ -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>
|
||||
|
|
|
@ -74,9 +74,9 @@ const JitsiScreen = ({
|
|||
<SafeAreaView
|
||||
edges = { safeAreaInsets }
|
||||
style = { styles.safeArea }>
|
||||
{ children }
|
||||
{children}
|
||||
</SafeAreaView>
|
||||
{ footerComponent && footerComponent() }
|
||||
{footerComponent && footerComponent()}
|
||||
</JitsiKeyboardAvoidingView>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -58,6 +58,7 @@ class LobbyScreen extends AbstractLobbyScreen<Props> {
|
|||
|
||||
return (
|
||||
<JitsiScreen
|
||||
safeAreaInsets = { [ 'left' ] }
|
||||
style = { contentWrapperStyles }>
|
||||
<BrandingImageBackground />
|
||||
<View style = { largeVideoContainerStyles }>
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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') }
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue