You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Prejoin.tsx 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { useIsFocused } from '@react-navigation/native';
  2. import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. import {
  5. BackHandler,
  6. Platform,
  7. StyleProp,
  8. Text,
  9. TextStyle,
  10. View,
  11. ViewStyle
  12. } from 'react-native';
  13. import { useDispatch, useSelector } from 'react-redux';
  14. import { appNavigate } from '../../../app/actions.native';
  15. import { IReduxState } from '../../../app/types';
  16. import { setAudioOnly } from '../../../base/audio-only/actions';
  17. import { getConferenceName } from '../../../base/conference/functions';
  18. import { connect } from '../../../base/connection/actions.native';
  19. import { PREJOIN_PAGE_HIDE_DISPLAY_NAME } from '../../../base/flags/constants';
  20. import { getFeatureFlag } from '../../../base/flags/functions';
  21. import { IconCloseLarge } from '../../../base/icons/svg';
  22. import JitsiScreen from '../../../base/modal/components/JitsiScreen';
  23. import { getLocalParticipant } from '../../../base/participants/functions';
  24. import { getFieldValue } from '../../../base/react/functions';
  25. import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
  26. import { updateSettings } from '../../../base/settings/actions';
  27. import Button from '../../../base/ui/components/native/Button';
  28. import Input from '../../../base/ui/components/native/Input';
  29. import { BUTTON_TYPES } from '../../../base/ui/constants.native';
  30. import BrandingImageBackground from '../../../dynamic-branding/components/native/BrandingImageBackground';
  31. import LargeVideo from '../../../large-video/components/LargeVideo.native';
  32. import HeaderNavigationButton from '../../../mobile/navigation/components/HeaderNavigationButton';
  33. import { navigateRoot } from '../../../mobile/navigation/rootNavigationContainerRef';
  34. import { screen } from '../../../mobile/navigation/routes';
  35. import AudioMuteButton from '../../../toolbox/components/native/AudioMuteButton';
  36. import VideoMuteButton from '../../../toolbox/components/native/VideoMuteButton';
  37. import { isDisplayNameRequired } from '../../functions';
  38. import { IPrejoinProps } from '../../types';
  39. import { preJoinStyles as styles } from './styles';
  40. const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
  41. const dispatch = useDispatch();
  42. const isFocused = useIsFocused();
  43. const { t } = useTranslation();
  44. const aspectRatio = useSelector(
  45. (state: IReduxState) => state['features/base/responsive-ui']?.aspectRatio
  46. );
  47. const localParticipant = useSelector((state: IReduxState) => getLocalParticipant(state));
  48. const isDisplayNameMandatory = useSelector((state: IReduxState) => isDisplayNameRequired(state));
  49. const isDisplayNameVisible
  50. = useSelector((state: IReduxState) => !getFeatureFlag(state, PREJOIN_PAGE_HIDE_DISPLAY_NAME, false));
  51. const roomName = useSelector((state: IReduxState) => getConferenceName(state));
  52. const participantName = localParticipant?.name;
  53. const [ displayName, setDisplayName ]
  54. = useState(participantName || '');
  55. const [ isJoining, setIsJoining ]
  56. = useState(false);
  57. const onChangeDisplayName = useCallback(event => {
  58. const fieldValue = getFieldValue(event);
  59. setDisplayName(fieldValue);
  60. dispatch(updateSettings({
  61. displayName: fieldValue
  62. }));
  63. }, [ displayName ]);
  64. const onJoin = useCallback(() => {
  65. setIsJoining(true);
  66. dispatch(connect());
  67. navigateRoot(screen.conference.root);
  68. }, [ dispatch ]);
  69. const onJoinLowBandwidth = useCallback(() => {
  70. dispatch(setAudioOnly(true));
  71. onJoin();
  72. }, [ dispatch ]);
  73. const goBack = useCallback(() => {
  74. dispatch(appNavigate(undefined));
  75. return true;
  76. }, [ dispatch ]);
  77. const headerLeft = () => {
  78. if (Platform.OS === 'ios') {
  79. return (
  80. <HeaderNavigationButton
  81. label = { t('dialog.close') }
  82. onPress = { goBack } />
  83. );
  84. }
  85. return (
  86. <HeaderNavigationButton
  87. onPress = { goBack }
  88. src = { IconCloseLarge } />
  89. );
  90. };
  91. const { PRIMARY, TERTIARY } = BUTTON_TYPES;
  92. const joinButtonDisabled = !displayName && isDisplayNameMandatory;
  93. useEffect(() => {
  94. BackHandler.addEventListener('hardwareBackPress', goBack);
  95. return () => BackHandler.removeEventListener('hardwareBackPress', goBack);
  96. }, []);
  97. useLayoutEffect(() => {
  98. navigation.setOptions({
  99. headerLeft,
  100. headerTitle: t('prejoin.joinMeeting')
  101. });
  102. }, [ navigation ]);
  103. let contentWrapperStyles;
  104. let contentContainerStyles;
  105. let largeVideoContainerStyles;
  106. if (aspectRatio === ASPECT_RATIO_NARROW) {
  107. contentWrapperStyles = styles.contentWrapper;
  108. contentContainerStyles = styles.contentContainer;
  109. largeVideoContainerStyles = styles.largeVideoContainer;
  110. } else {
  111. contentWrapperStyles = styles.contentWrapperWide;
  112. contentContainerStyles = styles.contentContainerWide;
  113. largeVideoContainerStyles = styles.largeVideoContainerWide;
  114. }
  115. return (
  116. <JitsiScreen
  117. addBottomPadding = { false }
  118. safeAreaInsets = { [ 'right' ] }
  119. style = { contentWrapperStyles }>
  120. <BrandingImageBackground />
  121. {
  122. isFocused
  123. && <View style = { largeVideoContainerStyles }>
  124. <View style = { styles.displayRoomNameBackdrop as StyleProp<TextStyle> }>
  125. <Text
  126. numberOfLines = { 1 }
  127. style = { styles.preJoinRoomName as StyleProp<TextStyle> }>
  128. { roomName }
  129. </Text>
  130. </View>
  131. <LargeVideo />
  132. </View>
  133. }
  134. <View style = { contentContainerStyles as ViewStyle }>
  135. <View style = { styles.toolboxContainer as ViewStyle }>
  136. <AudioMuteButton
  137. styles = { styles.buttonStylesBorderless } />
  138. <VideoMuteButton
  139. styles = { styles.buttonStylesBorderless } />
  140. </View>
  141. {
  142. isDisplayNameVisible
  143. && <Input
  144. customStyles = {{ input: styles.customInput }}
  145. onChange = { onChangeDisplayName }
  146. placeholder = { t('dialog.enterDisplayName') }
  147. value = { displayName } />
  148. }
  149. <Button
  150. accessibilityLabel = 'prejoin.joinMeeting'
  151. disabled = { joinButtonDisabled }
  152. labelKey = 'prejoin.joinMeeting'
  153. onClick = { isJoining ? undefined : onJoin }
  154. style = { styles.joinButton }
  155. type = { PRIMARY } />
  156. <Button
  157. accessibilityLabel = 'prejoin.joinMeetingInLowBandwidthMode'
  158. disabled = { joinButtonDisabled }
  159. labelKey = 'prejoin.joinMeetingInLowBandwidthMode'
  160. onClick = { onJoinLowBandwidth }
  161. style = { styles.joinButton }
  162. type = { TERTIARY } />
  163. </View>
  164. </JitsiScreen>
  165. );
  166. };
  167. export default Prejoin;