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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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/AudioMuteButton';
  36. import VideoMuteButton from '../../../toolbox/components/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. {/* @ts-ignore */}
  132. <LargeVideo />
  133. </View>
  134. }
  135. <View style = { contentContainerStyles as ViewStyle }>
  136. <View style = { styles.toolboxContainer as ViewStyle }>
  137. <AudioMuteButton
  138. styles = { styles.buttonStylesBorderless } />
  139. <VideoMuteButton
  140. styles = { styles.buttonStylesBorderless } />
  141. </View>
  142. {
  143. isDisplayNameVisible
  144. && <Input
  145. customStyles = {{ input: styles.customInput }}
  146. onChange = { onChangeDisplayName }
  147. placeholder = { t('dialog.enterDisplayName') }
  148. value = { displayName } />
  149. }
  150. <Button
  151. accessibilityLabel = 'prejoin.joinMeeting'
  152. disabled = { joinButtonDisabled }
  153. labelKey = 'prejoin.joinMeeting'
  154. onClick = { isJoining ? undefined : onJoin }
  155. style = { styles.joinButton }
  156. type = { PRIMARY } />
  157. <Button
  158. accessibilityLabel = 'prejoin.joinMeetingInLowBandwidthMode'
  159. disabled = { joinButtonDisabled }
  160. labelKey = 'prejoin.joinMeetingInLowBandwidthMode'
  161. onClick = { onJoinLowBandwidth }
  162. style = { styles.joinButton }
  163. type = { TERTIARY } />
  164. </View>
  165. </JitsiScreen>
  166. );
  167. };
  168. export default Prejoin;