Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

Whiteboard.tsx 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { Route } from '@react-navigation/native';
  2. import React, { PureComponent } from 'react';
  3. import { WithTranslation } from 'react-i18next';
  4. import { Platform, View, ViewStyle } from 'react-native';
  5. import { WebView } from 'react-native-webview';
  6. import { connect } from 'react-redux';
  7. import { IReduxState, IStore } from '../../../app/types';
  8. import { getCurrentConference } from '../../../base/conference/functions';
  9. import { IJitsiConference } from '../../../base/conference/reducer';
  10. import { openDialog } from '../../../base/dialog/actions';
  11. import { translate } from '../../../base/i18n/functions';
  12. import { IconCloseLarge } from '../../../base/icons/svg';
  13. import JitsiScreen from '../../../base/modal/components/JitsiScreen';
  14. import LoadingIndicator from '../../../base/react/components/native/LoadingIndicator';
  15. import { safeDecodeURIComponent } from '../../../base/util/uri';
  16. import HeaderNavigationButton
  17. from '../../../mobile/navigation/components/HeaderNavigationButton';
  18. import {
  19. goBack
  20. } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
  21. import { setupWhiteboard } from '../../actions.native';
  22. import { WHITEBOARD_ID } from '../../constants';
  23. import { getWhiteboardInfoForURIString } from '../../functions';
  24. import logger from '../../logger';
  25. import WhiteboardErrorDialog from './WhiteboardErrorDialog';
  26. import styles, { INDICATOR_COLOR } from './styles';
  27. interface IProps extends WithTranslation {
  28. /**
  29. * The current Jitsi conference.
  30. */
  31. conference?: IJitsiConference;
  32. /**
  33. * Redux store dispatch method.
  34. */
  35. dispatch: IStore['dispatch'];
  36. /**
  37. * Window location href.
  38. */
  39. locationHref: string;
  40. /**
  41. * Default prop for navigating between screen components(React Navigation).
  42. */
  43. navigation: any;
  44. /**
  45. * Default prop for navigating between screen components(React Navigation).
  46. */
  47. route: Route<'', {
  48. collabDetails: { roomId: string; roomKey: string; };
  49. collabServerUrl: string;
  50. localParticipantName: string;
  51. }>;
  52. }
  53. /**
  54. * Implements a React native component that displays the whiteboard page for a specific room.
  55. */
  56. class Whiteboard extends PureComponent<IProps> {
  57. /**
  58. * Initializes a new instance.
  59. *
  60. * @inheritdoc
  61. */
  62. constructor(props: IProps) {
  63. super(props);
  64. this._onError = this._onError.bind(this);
  65. this._onNavigate = this._onNavigate.bind(this);
  66. this._onMessage = this._onMessage.bind(this);
  67. this._renderLoading = this._renderLoading.bind(this);
  68. }
  69. /**
  70. * Implements React's {@link Component#componentDidMount()}. Invoked
  71. * immediately after mounting occurs.
  72. *
  73. * @inheritdoc
  74. * @returns {void}
  75. */
  76. componentDidMount() {
  77. const { navigation, t } = this.props;
  78. const headerLeft = () => {
  79. if (Platform.OS === 'ios') {
  80. return (
  81. <HeaderNavigationButton
  82. label = { t('dialog.close') }
  83. onPress = { goBack } />
  84. );
  85. }
  86. return (
  87. <HeaderNavigationButton
  88. onPress = { goBack }
  89. src = { IconCloseLarge } />
  90. );
  91. };
  92. navigation.setOptions({ headerLeft });
  93. }
  94. /**
  95. * Implements React's {@link Component#render()}.
  96. *
  97. * @inheritdoc
  98. */
  99. render() {
  100. const { locationHref, route } = this.props;
  101. const collabServerUrl = safeDecodeURIComponent(route.params?.collabServerUrl);
  102. const localParticipantName = safeDecodeURIComponent(route.params?.localParticipantName);
  103. const collabDetails = route.params?.collabDetails;
  104. const uri = getWhiteboardInfoForURIString(
  105. locationHref,
  106. collabServerUrl,
  107. collabDetails,
  108. localParticipantName
  109. ) ?? '';
  110. return (
  111. <JitsiScreen
  112. safeAreaInsets = { [ 'bottom', 'left', 'right' ] }
  113. style = { styles.backDrop }>
  114. <WebView
  115. domStorageEnabled = { false }
  116. incognito = { true }
  117. javaScriptEnabled = { true }
  118. nestedScrollEnabled = { true }
  119. onError = { this._onError }
  120. onMessage = { this._onMessage }
  121. onShouldStartLoadWithRequest = { this._onNavigate }
  122. renderLoading = { this._renderLoading }
  123. scrollEnabled = { true }
  124. setSupportMultipleWindows = { false }
  125. source = {{ uri }}
  126. startInLoadingState = { true }
  127. style = { styles.webView }
  128. webviewDebuggingEnabled = { true } />
  129. </JitsiScreen>
  130. );
  131. }
  132. /**
  133. * Callback to handle the error if the page fails to load.
  134. *
  135. * @returns {void}
  136. */
  137. _onError() {
  138. this.props.dispatch(openDialog(WhiteboardErrorDialog));
  139. }
  140. /**
  141. * Callback to intercept navigation inside the webview and make the native app handle the whiteboard requests.
  142. *
  143. * NOTE: We don't navigate to anywhere else from that view.
  144. *
  145. * @param {any} request - The request object.
  146. * @returns {boolean}
  147. */
  148. _onNavigate(request: { url: string; }) {
  149. const { url } = request;
  150. const { locationHref, route } = this.props;
  151. const collabServerUrl = route.params?.collabServerUrl;
  152. const collabDetails = route.params?.collabDetails;
  153. const localParticipantName = route.params?.localParticipantName;
  154. return url === getWhiteboardInfoForURIString(
  155. locationHref,
  156. collabServerUrl,
  157. collabDetails,
  158. localParticipantName
  159. );
  160. }
  161. /**
  162. * Callback to handle the message events.
  163. *
  164. * @param {any} event - The event.
  165. * @returns {void}
  166. */
  167. _onMessage(event: any) {
  168. const { conference, dispatch } = this.props;
  169. const collabData = JSON.parse(event.nativeEvent.data);
  170. if (!collabData) {
  171. logger.error('Message payload is missing whiteboard collaboration data');
  172. return;
  173. }
  174. const { collabDetails, collabServerUrl } = collabData;
  175. if (collabDetails?.roomId && collabDetails?.roomKey && collabServerUrl) {
  176. dispatch(setupWhiteboard({
  177. collabDetails,
  178. collabServerUrl
  179. }));
  180. // Broadcast the collab details.
  181. conference?.getMetadataHandler().setMetadata(WHITEBOARD_ID, {
  182. collabServerUrl,
  183. collabDetails
  184. });
  185. }
  186. }
  187. /**
  188. * Renders the loading indicator.
  189. *
  190. * @returns {React$Component<any>}
  191. */
  192. _renderLoading() {
  193. return (
  194. <View style = { styles.indicatorWrapper as ViewStyle }>
  195. <LoadingIndicator
  196. color = { INDICATOR_COLOR }
  197. size = 'large' />
  198. </View>
  199. );
  200. }
  201. }
  202. /**
  203. * Maps (parts of) the redux state to the associated
  204. * {@code WaitForOwnerDialog}'s props.
  205. *
  206. * @param {Object} state - The redux state.
  207. * @private
  208. * @returns {IProps}
  209. */
  210. function mapStateToProps(state: IReduxState) {
  211. const { locationURL } = state['features/base/connection'];
  212. const { href = '' } = locationURL ?? {};
  213. return {
  214. conference: getCurrentConference(state),
  215. locationHref: href
  216. };
  217. }
  218. export default translate(connect(mapStateToProps)(Whiteboard));