feat(lobby/native) LobbyScreen and LobbyChatScreen
This commit is contained in:
parent
037b9202a6
commit
3e004811e0
|
@ -37,6 +37,11 @@ type Props = {
|
|||
*/
|
||||
hasTabNavigator?: boolean,
|
||||
|
||||
/**
|
||||
* Insets for the SafeAreaView.
|
||||
*/
|
||||
safeAreaInsets?: Array,
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the KeyboardAvoidingView containing the content of the modal.
|
||||
*/
|
||||
|
@ -49,6 +54,7 @@ const JitsiScreen = ({
|
|||
footerComponent,
|
||||
hasTabNavigator = false,
|
||||
hasBottomTextInput = false,
|
||||
safeAreaInsets = [ 'bottom', 'left', 'right' ],
|
||||
style
|
||||
}: Props) => (
|
||||
<View
|
||||
|
@ -59,11 +65,7 @@ const JitsiScreen = ({
|
|||
hasTabNavigator = { hasTabNavigator }
|
||||
style = { style }>
|
||||
<SafeAreaView
|
||||
edges = { [
|
||||
'bottom',
|
||||
'left',
|
||||
'right'
|
||||
] }
|
||||
edges = { safeAreaInsets }
|
||||
style = { styles.safeArea }>
|
||||
{ children }
|
||||
</SafeAreaView>
|
||||
|
|
|
@ -183,7 +183,7 @@ class Conference extends AbstractConference<Props, State> {
|
|||
const { _showLobby } = this.props;
|
||||
|
||||
if (!prevProps._showLobby && _showLobby) {
|
||||
navigate(screen.lobby);
|
||||
navigate(screen.lobby.root);
|
||||
}
|
||||
|
||||
if (prevProps._showLobby && !_showLobby) {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { connect } from '../../../base/redux';
|
||||
import ChatInputBar from '../../../chat/components/native/ChatInputBar';
|
||||
import MessageContainer from '../../../chat/components/native/MessageContainer';
|
||||
import AbstractLobbyScreen, {
|
||||
Props as AbstractProps,
|
||||
_mapStateToProps as abstractMapStateToProps
|
||||
} from '../AbstractLobbyScreen';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* Implements a chat screen that appears when communication is started
|
||||
* between the moderator and the participant being in the lobby.
|
||||
*/
|
||||
class LobbyChatScreen extends
|
||||
AbstractLobbyScreen<AbstractProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _lobbyChatMessages } = this.props;
|
||||
|
||||
return (
|
||||
<JitsiScreen style = { styles.lobbyChatWrapper }>
|
||||
<MessageContainer messages = { _lobbyChatMessages } />
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
_onSendMessage: () => void;
|
||||
}
|
||||
|
||||
export default translate(connect(abstractMapStateToProps)(LobbyChatScreen));
|
|
@ -2,50 +2,50 @@
|
|||
|
||||
import React from 'react';
|
||||
import { Text, View, TouchableOpacity, TextInput } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconClose, IconEdit } from '../../../base/icons';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { LoadingIndicator } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import ChatInputBar from '../../../chat/components/native/ChatInputBar';
|
||||
import MessageContainer from '../../../chat/components/native/MessageContainer';
|
||||
import AbstractLobbyScreen, { _mapStateToProps } from '../AbstractLobbyScreen';
|
||||
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme';
|
||||
import InviteButton
|
||||
from '../../../invite/components/add-people-dialog/native/InviteButton';
|
||||
import { LargeVideo } from '../../../large-video/components';
|
||||
import { navigate }
|
||||
from '../../../mobile/navigation/components/lobby/LobbyNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import AudioMuteButton from '../../../toolbox/components/AudioMuteButton';
|
||||
import VideoMuteButton from '../../../toolbox/components/VideoMuteButton';
|
||||
import AbstractLobbyScreen, {
|
||||
Props as AbstractProps,
|
||||
_mapStateToProps as abstractMapStateToProps } from '../AbstractLobbyScreen';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* The current aspect ratio of the screen.
|
||||
*/
|
||||
_aspectRatio: Symbol
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a waiting screen that represents the participant being in the lobby.
|
||||
*/
|
||||
class LobbyScreen extends AbstractLobbyScreen {
|
||||
class LobbyScreen extends AbstractLobbyScreen<Props> {
|
||||
/**
|
||||
* Implements {@code PureComponent#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _meetingName, t } = this.props;
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
style = { this.props._isLobbyChatActive && this.state.isChatOpen
|
||||
? styles.lobbyChatWrapper
|
||||
: styles.contentWrapper }>
|
||||
{this.props._isLobbyChatActive && this.state.isChatOpen
|
||||
? this._renderLobbyChat()
|
||||
: <SafeAreaView>
|
||||
<Text style = { styles.dialogTitle }>
|
||||
{ t(this._getScreenTitleKey(), { moderator: this.props._lobbyMessageRecipient }) }
|
||||
</Text>
|
||||
|
||||
<Text style = { styles.secondaryText }>
|
||||
{ _meetingName }
|
||||
</Text>
|
||||
{ this._renderContent()}
|
||||
</SafeAreaView> }
|
||||
</JitsiScreen>
|
||||
<>
|
||||
{ this._renderLobby() }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -69,35 +69,58 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
|
||||
_onSwitchToPasswordMode: () => void;
|
||||
|
||||
_onSendMessage: () => void;
|
||||
|
||||
_onToggleChat: () => void;
|
||||
|
||||
_renderContent: () => React$Element<*>;
|
||||
|
||||
_renderToolbarButtons: () => React$Element<*>;
|
||||
|
||||
_renderLobby: () => React$Element<*>;
|
||||
|
||||
_onNavigateToLobbyChat: () => void;
|
||||
|
||||
/**
|
||||
* Renders the lobby chat.
|
||||
* Navigates to the lobby chat screen.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onNavigateToLobbyChat() {
|
||||
navigate(screen.lobby.chat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the lobby.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderLobbyChat() {
|
||||
const { t } = this.props;
|
||||
_renderLobby() {
|
||||
const { _aspectRatio } = this.props;
|
||||
let contentStyles;
|
||||
let largeVideoContainerStyles;
|
||||
let contentContainerStyles;
|
||||
|
||||
if (_aspectRatio === ASPECT_RATIO_NARROW) {
|
||||
largeVideoContainerStyles = styles.largeVideoContainer;
|
||||
contentContainerStyles = styles.contentContainer;
|
||||
} else {
|
||||
contentStyles = styles.contentWide;
|
||||
largeVideoContainerStyles = styles.largeVideoContainerWide;
|
||||
contentContainerStyles = styles.contentContainerWide;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style = { styles.lobbyChatHeader }>
|
||||
<Text style = { styles.lobbyChatTitle }>
|
||||
{ t(this._getScreenTitleKey(), { moderator: this.props._lobbyMessageRecipient }) }
|
||||
</Text>
|
||||
<TouchableOpacity onPress = { this._onToggleChat }>
|
||||
<Icon
|
||||
src = { IconClose }
|
||||
style = { styles.lobbyChatCloseButton } />
|
||||
</TouchableOpacity>
|
||||
<JitsiScreen
|
||||
safeAreaInsets = { [ 'right' ] }
|
||||
style = { styles.contentWrapper }>
|
||||
<View style = { contentStyles }>
|
||||
<View style = { largeVideoContainerStyles }>
|
||||
<LargeVideo />
|
||||
</View>
|
||||
<View style = { contentContainerStyles }>
|
||||
{ this._renderContent() }
|
||||
{ this._renderToolbarButtons() }
|
||||
</View>
|
||||
</View>
|
||||
<MessageContainer messages = { this.props._lobbyChatMessages } />
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
</>
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -108,15 +131,15 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
*/
|
||||
_renderJoining() {
|
||||
return (
|
||||
<>
|
||||
<View style = { styles.formWrapper }>
|
||||
<LoadingIndicator
|
||||
color = 'black'
|
||||
color = { BaseTheme.palette.icon01 }
|
||||
style = { styles.loadingIndicator } />
|
||||
<Text style = { styles.joiningMessage }>
|
||||
{ this.props.t('lobby.joiningMessage') }
|
||||
</Text>
|
||||
{ this._renderStandardButtons() }
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -127,7 +150,7 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
*/
|
||||
_renderParticipantForm() {
|
||||
const { t } = this.props;
|
||||
const { displayName, email } = this.state;
|
||||
const { displayName } = this.state;
|
||||
|
||||
return (
|
||||
<View style = { styles.formWrapper }>
|
||||
|
@ -138,13 +161,6 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
onChangeText = { this._onChangeDisplayName }
|
||||
style = { styles.field }
|
||||
value = { displayName } />
|
||||
<Text style = { styles.fieldLabel }>
|
||||
{ t('lobby.emailField') }
|
||||
</Text>
|
||||
<TextInput
|
||||
onChangeText = { this._onChangeEmail }
|
||||
style = { styles.field }
|
||||
value = { email } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -155,28 +171,7 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
* @inheritdoc
|
||||
*/
|
||||
_renderParticipantInfo() {
|
||||
const { displayName, email } = this.state;
|
||||
|
||||
return (
|
||||
<View style = { styles.participantBox }>
|
||||
<TouchableOpacity
|
||||
onPress = { this._onEnableEdit }
|
||||
style = { styles.editButton }>
|
||||
<Icon
|
||||
src = { IconEdit }
|
||||
style = { styles.editIcon } />
|
||||
</TouchableOpacity>
|
||||
<Avatar
|
||||
participantId = { this.props._participantId }
|
||||
size = { 64 } />
|
||||
<Text style = { styles.displayNameText }>
|
||||
{ displayName }
|
||||
</Text>
|
||||
{ Boolean(email) && <Text style = { styles.secondaryText }>
|
||||
{ email }
|
||||
</Text> }
|
||||
</View>
|
||||
);
|
||||
return this._renderParticipantForm();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,7 +210,17 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style = { styles.passwordJoinButtonsWrapper }>
|
||||
<TouchableOpacity
|
||||
onPress = { this._onSwitchToKnockMode }
|
||||
style = { [
|
||||
styles.button,
|
||||
styles.primaryButton
|
||||
] }>
|
||||
<Text style = { styles.primaryButtonText }>
|
||||
{ t('lobby.backToKnockModeButton') }
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
disabled = { !this.state.password }
|
||||
onPress = { this._onJoinWithPassword }
|
||||
|
@ -227,17 +232,34 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
{ t('lobby.passwordJoinButton') }
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress = { this._onSwitchToKnockMode }
|
||||
style = { [
|
||||
styles.button,
|
||||
styles.secondaryButton
|
||||
] }>
|
||||
<Text>
|
||||
{ t('lobby.backToKnockModeButton') }
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the toolbar buttons menu.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderToolbarButtons() {
|
||||
const { _aspectRatio } = this.props;
|
||||
let toolboxContainerStyles;
|
||||
|
||||
if (_aspectRatio === ASPECT_RATIO_NARROW) {
|
||||
toolboxContainerStyles = styles.toolboxContainer;
|
||||
} else {
|
||||
toolboxContainerStyles = styles.toolboxContainerWide;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { toolboxContainerStyles }>
|
||||
<AudioMuteButton
|
||||
styles = { styles.buttonStylesBorderless } />
|
||||
<VideoMuteButton
|
||||
styles = { styles.buttonStylesBorderless } />
|
||||
<InviteButton
|
||||
styles = { styles.buttonStylesBorderless } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -248,50 +270,72 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
*/
|
||||
_renderStandardButtons() {
|
||||
const { _knocking, _renderPassword, _isLobbyChatActive, t } = this.props;
|
||||
const { displayName } = this.state;
|
||||
const askToJoinButtonStyles
|
||||
= displayName ? styles.primaryButton : styles.primaryButtonDisabled;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ _knocking || <TouchableOpacity
|
||||
disabled = { !this.state.displayName }
|
||||
onPress = { this._onAskToJoin }
|
||||
<View style = { styles.standardButtonWrapper }>
|
||||
{ _knocking && _isLobbyChatActive && <TouchableOpacity
|
||||
onPress = { this._onNavigateToLobbyChat }
|
||||
style = { [
|
||||
styles.button,
|
||||
styles.primaryButton
|
||||
] }>
|
||||
<Text style = { styles.primaryButtonText }>
|
||||
{ t('lobby.knockButton') }
|
||||
</Text>
|
||||
</TouchableOpacity> }
|
||||
{ _knocking && _isLobbyChatActive && <TouchableOpacity
|
||||
onPress = { this._onToggleChat }
|
||||
style = { [
|
||||
styles.button,
|
||||
styles.secondaryButton
|
||||
] }>
|
||||
<Text>
|
||||
{ t('toolbar.openChat') }
|
||||
</Text>
|
||||
</TouchableOpacity>}
|
||||
{ _knocking || <TouchableOpacity
|
||||
disabled = { !displayName }
|
||||
onPress = { this._onAskToJoin }
|
||||
style = { [
|
||||
styles.button,
|
||||
askToJoinButtonStyles
|
||||
] }>
|
||||
<Text style = { styles.primaryButtonText }>
|
||||
{ t('lobby.knockButton') }
|
||||
</Text>
|
||||
</TouchableOpacity> }
|
||||
{ _renderPassword && <TouchableOpacity
|
||||
onPress = { this._onSwitchToPasswordMode }
|
||||
style = { [
|
||||
styles.button,
|
||||
styles.secondaryButton
|
||||
styles.primaryButton
|
||||
] }>
|
||||
<Text>
|
||||
<Text style = { styles.primaryButtonText }>
|
||||
{ t('lobby.enterPasswordButton') }
|
||||
</Text>
|
||||
</TouchableOpacity> }
|
||||
<TouchableOpacity
|
||||
onPress = { this._onCancel }
|
||||
style = { styles.cancelButton }>
|
||||
<Text>
|
||||
style = { [
|
||||
styles.button,
|
||||
styles.cancelButton
|
||||
] }>
|
||||
<Text style = { styles.cancelButtonText }>
|
||||
{ t('dialog.Cancel') }
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Props} ownProps - The own props of the component.
|
||||
* @returns {{
|
||||
* _aspectRatio: Symbol
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object, ownProps: Props) {
|
||||
return {
|
||||
...abstractMapStateToProps(state, ownProps),
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(LobbyScreen));
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
|
||||
export { default as KnockingParticipantList } from './KnockingParticipantList';
|
||||
export { default as LobbyScreen } from './LobbyScreen';
|
||||
export { default as LobbyChatScreen } from './LobbyChatScreen';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
|
||||
const SECONDARY_COLOR = BaseTheme.palette.border04;
|
||||
|
||||
|
@ -8,8 +8,24 @@ export default {
|
|||
button: {
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
marginVertical: BaseTheme.spacing[1],
|
||||
paddingVertical: BaseTheme.spacing[2]
|
||||
padding: BaseTheme.spacing[2],
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
buttonStylesBorderless: {
|
||||
iconStyle: {
|
||||
backgroundColor: BaseTheme.palette.action02Active,
|
||||
color: BaseTheme.palette.icon01,
|
||||
fontSize: 24
|
||||
},
|
||||
style: {
|
||||
backgroundColor: BaseTheme.palette.action02Active,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
marginHorizontal: BaseTheme.spacing[3],
|
||||
height: 24,
|
||||
width: 24
|
||||
}
|
||||
},
|
||||
|
||||
lobbyChatWrapper: {
|
||||
|
@ -26,29 +42,66 @@ export default {
|
|||
},
|
||||
|
||||
lobbyChatTitle: {
|
||||
color: '#fff',
|
||||
color: BaseTheme.palette.text01,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
flexShrink: 1
|
||||
},
|
||||
|
||||
lobbyChatCloseButton: {
|
||||
fontSize: 20,
|
||||
marginLeft: 20,
|
||||
color: '#fff'
|
||||
fontSize: 24,
|
||||
marginLeft: BaseTheme.spacing[3],
|
||||
marginTop: BaseTheme.spacing[1],
|
||||
color: BaseTheme.palette.icon01
|
||||
},
|
||||
|
||||
contentWrapper: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyItems: 'center',
|
||||
height: '100%'
|
||||
backgroundColor: BaseTheme.palette.ui02,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
closeIcon: {
|
||||
color: 'red',
|
||||
fontSize: 20
|
||||
contentWide: {
|
||||
backgroundColor: BaseTheme.palette.ui02,
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
largeVideoContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
minHeight: '50%'
|
||||
},
|
||||
|
||||
largeVideoContainerWide: {
|
||||
height: '100%',
|
||||
width: '50%'
|
||||
},
|
||||
|
||||
contentContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
contentContainerWide: {
|
||||
justifyContent: 'center',
|
||||
marginHorizontal: BaseTheme.spacing[2],
|
||||
width: '50%'
|
||||
},
|
||||
|
||||
toolboxContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
marginTop: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
toolboxContainerWide: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
marginTop: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
dialogTitle: {
|
||||
|
@ -75,40 +128,55 @@ export default {
|
|||
},
|
||||
|
||||
field: {
|
||||
backgroundColor: BaseTheme.palette.field02,
|
||||
borderColor: SECONDARY_COLOR,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
marginVertical: 8,
|
||||
padding: 8
|
||||
borderRadius: BaseTheme.shape.borderRadius,
|
||||
borderWidth: 2,
|
||||
marginHorizontal: BaseTheme.spacing[3],
|
||||
padding: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
fieldError: {
|
||||
color: BaseTheme.palette.warning07,
|
||||
fontSize: 10
|
||||
},
|
||||
|
||||
fieldRow: {
|
||||
paddingTop: 16
|
||||
color: BaseTheme.palette.warning03,
|
||||
marginLeft: BaseTheme.spacing[3],
|
||||
fontSize: 16
|
||||
},
|
||||
|
||||
fieldLabel: {
|
||||
color: BaseTheme.palette.text01,
|
||||
marginVertical: BaseTheme.spacing[4],
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
formWrapper: {
|
||||
alignItems: 'stretch',
|
||||
alignSelf: 'stretch'
|
||||
},
|
||||
|
||||
standardButtonWrapper: {
|
||||
alignSelf: 'stretch',
|
||||
paddingVertical: 16
|
||||
marginHorizontal: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
joiningContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
joiningMessage: {
|
||||
color: 'rgba(0, 0, 0, .7)',
|
||||
paddingBottom: 36,
|
||||
color: BaseTheme.palette.text01,
|
||||
marginBottom: BaseTheme.spacing[2],
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
passwordJoinButtonsWrapper: {
|
||||
alignItems: 'stretch',
|
||||
alignSelf: 'stretch',
|
||||
marginHorizontal: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
loadingIndicator: {
|
||||
marginVertical: 36
|
||||
marginVertical: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
participantBox: {
|
||||
|
@ -122,29 +190,33 @@ export default {
|
|||
},
|
||||
|
||||
primaryButton: {
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: 'rgb(3, 118, 218)'
|
||||
backgroundColor: BaseTheme.palette.action01,
|
||||
marginTop: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
primaryButtonDisabled: {
|
||||
backgroundColor: BaseTheme.palette.action03Disabled,
|
||||
marginTop: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
primaryButtonText: {
|
||||
color: 'white'
|
||||
color: BaseTheme.palette.text01
|
||||
},
|
||||
|
||||
secondaryButton: {
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
|
||||
secondaryText: {
|
||||
color: 'rgba(0, 0, 0, .7)',
|
||||
primaryText: {
|
||||
color: BaseTheme.palette.text01,
|
||||
margin: 'auto',
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
cancelButton: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
marginVertical: 4
|
||||
backgroundColor: BaseTheme.palette.action02Disabled,
|
||||
marginTop: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
cancelButtonText: {
|
||||
color: BaseTheme.palette.text01
|
||||
},
|
||||
|
||||
// KnockingParticipantList
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @flow
|
||||
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// $FlowExpectedError
|
||||
export const conferenceNavigationRef = React.createRef();
|
||||
|
||||
/**
|
||||
|
@ -13,7 +10,6 @@ export const conferenceNavigationRef = React.createRef();
|
|||
* @returns {Function}
|
||||
*/
|
||||
export function navigate(name: string, params: Object) {
|
||||
// $FlowExpectedError
|
||||
return conferenceNavigationRef.current?.navigate(name, params);
|
||||
}
|
||||
|
||||
|
@ -23,7 +19,6 @@ export function navigate(name: string, params: Object) {
|
|||
* @returns {Function}
|
||||
*/
|
||||
export function goBack() {
|
||||
// $FlowExpectedError
|
||||
return conferenceNavigationRef.current?.goBack();
|
||||
}
|
||||
|
||||
|
@ -34,7 +29,6 @@ export function goBack() {
|
|||
* @returns {Function}
|
||||
*/
|
||||
export function setParams(params: Object) {
|
||||
// $FlowExpectedError
|
||||
return conferenceNavigationRef.current?.setParams(params);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @flow
|
||||
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
|
@ -13,7 +11,6 @@ import { SharedDocument } from '../../../../../etherpad';
|
|||
import { GifsMenu } from '../../../../../gifs/components';
|
||||
import AddPeopleDialog
|
||||
from '../../../../../invite/components/add-people-dialog/native/AddPeopleDialog';
|
||||
import LobbyScreen from '../../../../../lobby/components/native/LobbyScreen';
|
||||
import { ParticipantsPane } from '../../../../../participants-pane/components/native';
|
||||
import { StartLiveStreamDialog } from '../../../../../recording';
|
||||
import { StartRecordingDialog }
|
||||
|
@ -31,7 +28,6 @@ import {
|
|||
gifsMenuOptions,
|
||||
inviteScreenOptions,
|
||||
liveStreamScreenOptions,
|
||||
lobbyScreenOptions,
|
||||
navigationContainerTheme,
|
||||
participantsScreenOptions,
|
||||
recordingScreenOptions,
|
||||
|
@ -42,6 +38,8 @@ import {
|
|||
} from '../../../screenOptions';
|
||||
import ChatAndPollsNavigationContainer
|
||||
from '../../chat/components/ChatAndPollsNavigationContainer';
|
||||
import LobbyNavigationContainer
|
||||
from '../../lobby/components/LobbyNavigationContainer';
|
||||
import {
|
||||
conferenceNavigationRef
|
||||
} from '../ConferenceNavigationContainerRef';
|
||||
|
@ -134,9 +132,12 @@ const ConferenceNavigationContainer = () => {
|
|||
title: t('notify.gifsMenu')
|
||||
}} />
|
||||
<ConferenceStack.Screen
|
||||
component = { LobbyScreen }
|
||||
name = { screen.lobby }
|
||||
options = { lobbyScreenOptions } />
|
||||
component = { LobbyNavigationContainer }
|
||||
name = { screen.lobby.root }
|
||||
options = {{
|
||||
gestureEnabled: false,
|
||||
headerShown: false
|
||||
}} />
|
||||
<ConferenceStack.Screen
|
||||
component = { AddPeopleDialog }
|
||||
name = { screen.conference.invite }
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
|
||||
export const lobbyNavigationContainerRef = React.createRef();
|
||||
|
||||
/**
|
||||
* User defined navigation action included inside the reference to the container.
|
||||
*
|
||||
* @param {string} name - Destination name of the route that has been defined somewhere.
|
||||
* @param {Object} params - Params to pass to the destination route.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function navigate(name: string, params: Object) {
|
||||
return lobbyNavigationContainerRef.current?.navigate(name, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* User defined navigation action included inside the reference to the container.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function goBack() {
|
||||
return lobbyNavigationContainerRef.current?.goBack();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { LobbyChatScreen, LobbyScreen } from '../../../../../lobby';
|
||||
import { screen } from '../../../routes';
|
||||
import {
|
||||
lobbyChatScreenOptions,
|
||||
lobbyScreenOptions, navigationContainerTheme
|
||||
} from '../../../screenOptions';
|
||||
import { lobbyNavigationContainerRef } from '../LobbyNavigationContainerRef';
|
||||
|
||||
const LobbyStack = createStackNavigator();
|
||||
|
||||
|
||||
const LobbyNavigationContainer = () => {
|
||||
const { isLobbyChatActive }
|
||||
= useSelector(state => state['features/chat']);
|
||||
|
||||
return (
|
||||
<NavigationContainer
|
||||
independent = { true }
|
||||
ref = { lobbyNavigationContainerRef }
|
||||
theme = { navigationContainerTheme }>
|
||||
<LobbyStack.Navigator
|
||||
screenOptions = {{
|
||||
presentation: 'modal'
|
||||
}}>
|
||||
<LobbyStack.Screen
|
||||
component = { LobbyScreen }
|
||||
name = { screen.lobby.main }
|
||||
options = { lobbyScreenOptions } />
|
||||
{
|
||||
isLobbyChatActive
|
||||
&& <LobbyStack.Screen
|
||||
component = { LobbyChatScreen }
|
||||
name = { screen.lobby.chat }
|
||||
options = { lobbyChatScreenOptions } />
|
||||
}
|
||||
</LobbyStack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default LobbyNavigationContainer;
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import { IconClose } from '../../base/icons';
|
||||
|
||||
import HeaderNavigationButton from './components/HeaderNavigationButton';
|
||||
|
||||
/**
|
||||
* Close icon/text button based on platform.
|
||||
*
|
||||
* @param {Function} goBack - Goes back to the previous screen function.
|
||||
* @returns {React.Component}
|
||||
*/
|
||||
export function screenHeaderCloseButton(goBack: Function) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
label = { t('dialog.close') }
|
||||
onPress = { goBack } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
onPress = { goBack }
|
||||
src = { IconClose } />
|
||||
);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import { SET_ROOM } from '../../base/conference/actionTypes';
|
||||
import { CONFERENCE_FAILED, SET_ROOM } from '../../base/conference/actionTypes';
|
||||
import { JitsiConferenceErrors } from '../../base/lib-jitsi-meet';
|
||||
import { MiddlewareRegistry } from '../../base/redux';
|
||||
import { readyToClose } from '../external-api/actions';
|
||||
|
||||
|
@ -16,6 +15,9 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
switch (action.type) {
|
||||
case SET_ROOM:
|
||||
return _setRoom(store, next, action);
|
||||
|
||||
case CONFERENCE_FAILED:
|
||||
return _conferenceFailed(store, next, action);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
@ -61,3 +63,31 @@ function _setRoom({ dispatch, getState }, next, action) {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to handle the conference failed event and navigate the user to the lobby screen
|
||||
* based on the failure reason.
|
||||
*
|
||||
* @param {Object} store - The Redux store.
|
||||
* @param {Function} next - The Redux next function.
|
||||
* @param {Object} action - The Redux action.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _conferenceFailed({ dispatch, getState }, next, action) {
|
||||
const state = getState();
|
||||
const isWelcomePageEnabled = isWelcomePageAppEnabled(state);
|
||||
const { error } = action;
|
||||
|
||||
// We need to cover the case where knocking participant
|
||||
// is rejected from entering the conference
|
||||
if (error.name === JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED) {
|
||||
if (isWelcomePageEnabled) {
|
||||
navigateRoot(screen.root);
|
||||
} else {
|
||||
// For JitsiSDK, WelcomePage is not available
|
||||
_sendReadyToClose(dispatch);
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
}
|
||||
|
|
|
@ -34,5 +34,9 @@ export const screen = {
|
|||
invite: 'Invite',
|
||||
sharedDocument: 'Shared document'
|
||||
},
|
||||
lobby: 'Lobby'
|
||||
lobby: {
|
||||
root: 'Lobby root',
|
||||
main: 'Lobby',
|
||||
chat: 'Lobby chat'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
// @flow
|
||||
|
||||
import { TransitionPresets } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import {
|
||||
Icon,
|
||||
IconClose,
|
||||
IconHelp,
|
||||
IconHome,
|
||||
IconInfo,
|
||||
|
@ -15,8 +11,9 @@ import {
|
|||
} from '../../base/icons';
|
||||
import BaseTheme from '../../base/ui/components/BaseTheme.native';
|
||||
|
||||
import HeaderNavigationButton from './components/HeaderNavigationButton';
|
||||
import { goBack } from './components/conference/ConferenceNavigationContainerRef';
|
||||
import { goBack as goBackToLobbyScreen } from './components/lobby/LobbyNavigationContainerRef';
|
||||
import { screenHeaderCloseButton } from './functions';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -167,16 +164,12 @@ export const helpScreenOptions = {
|
|||
/**
|
||||
* Screen options for conference.
|
||||
*/
|
||||
export const conferenceScreenOptions = {
|
||||
...fullScreenOptions
|
||||
};
|
||||
export const conferenceScreenOptions = fullScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for lobby modal.
|
||||
*/
|
||||
export const lobbyScreenOptions = {
|
||||
...fullScreenOptions
|
||||
};
|
||||
export const lobbyScreenOptions = fullScreenOptions;
|
||||
|
||||
/**
|
||||
* Tab bar options for chat screen.
|
||||
|
@ -198,23 +191,7 @@ export const chatTabBarOptions = {
|
|||
export const presentationScreenOptions = {
|
||||
...conferenceModalPresentation,
|
||||
headerBackTitleVisible: false,
|
||||
headerLeft: () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
label = { t('dialog.close') }
|
||||
onPress = { goBack } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
onPress = { goBack }
|
||||
src = { IconClose } />
|
||||
);
|
||||
},
|
||||
headerLeft: () => screenHeaderCloseButton(goBack),
|
||||
headerStatusBarHeight: 0,
|
||||
headerStyle: {
|
||||
backgroundColor: BaseTheme.palette.screen01Header
|
||||
|
@ -227,50 +204,44 @@ export const presentationScreenOptions = {
|
|||
/**
|
||||
* Screen options for chat.
|
||||
*/
|
||||
export const chatScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
};
|
||||
export const chatScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for invite modal.
|
||||
*/
|
||||
export const inviteScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
};
|
||||
export const inviteScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for participants modal.
|
||||
*/
|
||||
export const participantsScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
};
|
||||
export const participantsScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for speaker stats modal.
|
||||
*/
|
||||
export const speakerStatsScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
};
|
||||
export const speakerStatsScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for security options modal.
|
||||
*/
|
||||
export const securityScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
};
|
||||
export const securityScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for recording modal.
|
||||
*/
|
||||
export const recordingScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
};
|
||||
export const recordingScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for live stream modal.
|
||||
*/
|
||||
export const liveStreamScreenOptions = {
|
||||
...presentationScreenOptions
|
||||
export const liveStreamScreenOptions = presentationScreenOptions;
|
||||
|
||||
/**
|
||||
* Screen options for lobby chat modal.
|
||||
*/
|
||||
export const lobbyChatScreenOptions = {
|
||||
...presentationScreenOptions,
|
||||
headerLeft: () => screenHeaderCloseButton(goBackToLobbyScreen)
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue