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

AbstractStartLiveStreamDialog.js 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // @flow
  2. import React, { Component } from 'react';
  3. import {
  4. createRecordingDialogEvent,
  5. sendAnalytics
  6. } from '../../../analytics';
  7. import { Dialog } from '../../../base/dialog';
  8. import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
  9. /**
  10. * The type of the React {@code Component} props of
  11. * {@link AbstractStartLiveStreamDialog}.
  12. */
  13. export type Props = {
  14. /**
  15. * The {@code JitsiConference} for the current conference.
  16. */
  17. _conference: Object,
  18. /**
  19. * The current state of interactions with the Google API. Determines what
  20. * Google related UI should display.
  21. */
  22. _googleAPIState: number,
  23. /**
  24. * The email of the user currently logged in to the Google web client
  25. * application.
  26. */
  27. _googleProfileEmail: string,
  28. /**
  29. * The live stream key that was used before.
  30. */
  31. _streamKey: string,
  32. /**
  33. * The Redux dispatch function.
  34. */
  35. dispatch: Function,
  36. /**
  37. * Invoked to obtain translated strings.
  38. */
  39. t: Function
  40. }
  41. /**
  42. * The type of the React {@code Component} state of
  43. * {@link AbstractStartLiveStreamDialog}.
  44. */
  45. export type State = {
  46. /**
  47. * Details about the broadcasts available for use for the logged in Google
  48. * user's YouTube account.
  49. */
  50. broadcasts: ?Array<Object>,
  51. /**
  52. * The error type, as provided by Google, for the most recent error
  53. * encountered by the Google API.
  54. */
  55. errorType: ?string,
  56. /**
  57. * The boundStreamID of the broadcast currently selected in the broadcast
  58. * dropdown.
  59. */
  60. selectedBoundStreamID: ?string,
  61. /**
  62. * The selected or entered stream key to use for YouTube live streaming.
  63. */
  64. streamKey: string
  65. };
  66. /**
  67. * Implements an abstract class for the StartLiveStreamDialog on both platforms.
  68. *
  69. * NOTE: Google log-in is not supported for mobile yet for later implementation
  70. * but the abstraction of its properties are already present in this abstract
  71. * class.
  72. */
  73. export default class AbstractStartLiveStreamDialog<P: Props>
  74. extends Component<P, State> {
  75. _isMounted: boolean;
  76. /**
  77. * Constructor of the component.
  78. *
  79. * @inheritdoc
  80. */
  81. constructor(props: P) {
  82. super(props);
  83. this.state = {
  84. broadcasts: undefined,
  85. errorType: undefined,
  86. selectedBoundStreamID: undefined,
  87. streamKey: ''
  88. };
  89. /**
  90. * Instance variable used to flag whether the component is or is not
  91. * mounted. Used as a hack to avoid setting state on an unmounted
  92. * component.
  93. *
  94. * @private
  95. * @type {boolean}
  96. */
  97. this._isMounted = false;
  98. this._onCancel = this._onCancel.bind(this);
  99. this._onStreamKeyChange = this._onStreamKeyChange.bind(this);
  100. this._onSubmit = this._onSubmit.bind(this);
  101. }
  102. /**
  103. * Implements {@link Component#componentDidMount()}. Invoked immediately
  104. * after this component is mounted.
  105. *
  106. * @inheritdoc
  107. * @returns {void}
  108. */
  109. componentDidMount() {
  110. this._isMounted = true;
  111. }
  112. /**
  113. * Implements React's {@link Component#componentWillUnmount()}. Invoked
  114. * immediately before this component is unmounted and destroyed.
  115. *
  116. * @inheritdoc
  117. */
  118. componentWillUnmount() {
  119. this._isMounted = false;
  120. }
  121. /**
  122. * Implements {@code Component}'s render.
  123. *
  124. * @inheritdoc
  125. */
  126. render() {
  127. return (
  128. <Dialog
  129. cancelTitleKey = 'dialog.Cancel'
  130. okTitleKey = 'dialog.startLiveStreaming'
  131. onCancel = { this._onCancel }
  132. onSubmit = { this._onSubmit }
  133. titleKey = 'liveStreaming.start'
  134. width = { 'small' }>
  135. {
  136. this._renderDialogContent()
  137. }
  138. </Dialog>
  139. );
  140. }
  141. _onCancel: () => boolean;
  142. /**
  143. * Invokes the passed in {@link onCancel} callback and closes
  144. * {@code StartLiveStreamDialog}.
  145. *
  146. * @private
  147. * @returns {boolean} True is returned to close the modal.
  148. */
  149. _onCancel() {
  150. sendAnalytics(createRecordingDialogEvent('start', 'cancel.button'));
  151. return true;
  152. }
  153. /**
  154. * Asks the user to sign in, if not already signed in, and then requests a
  155. * list of the user's YouTube broadcasts.
  156. *
  157. * NOTE: To be implemented by platforms.
  158. *
  159. * @private
  160. * @returns {Promise}
  161. */
  162. _onGetYouTubeBroadcasts: () => Promise<*>;
  163. _onStreamKeyChange: string => void;
  164. /**
  165. * Callback invoked to update the {@code StartLiveStreamDialog} component's
  166. * display of the entered YouTube stream key.
  167. *
  168. * @param {string} streamKey - The stream key entered in the field.
  169. * @private
  170. * @returns {void}
  171. */
  172. _onStreamKeyChange(streamKey) {
  173. this._setStateIfMounted({
  174. streamKey,
  175. selectedBoundStreamID: undefined
  176. });
  177. }
  178. _onSubmit: () => boolean;
  179. /**
  180. * Invokes the passed in {@link onSubmit} callback with the entered stream
  181. * key, and then closes {@code StartLiveStreamDialog}.
  182. *
  183. * @private
  184. * @returns {boolean} False if no stream key is entered to preventing
  185. * closing, true to close the modal.
  186. */
  187. _onSubmit() {
  188. const { broadcasts, selectedBoundStreamID } = this.state;
  189. const key = this.state.streamKey || this.props._streamKey;
  190. if (!key) {
  191. return false;
  192. }
  193. let selectedBroadcastID = null;
  194. if (selectedBoundStreamID) {
  195. const selectedBroadcast = broadcasts && broadcasts.find(
  196. broadcast => broadcast.boundStreamID === selectedBoundStreamID);
  197. selectedBroadcastID = selectedBroadcast && selectedBroadcast.id;
  198. }
  199. sendAnalytics(
  200. createRecordingDialogEvent('start', 'confirm.button'));
  201. this.props._conference.startRecording({
  202. broadcastId: selectedBroadcastID,
  203. mode: JitsiRecordingConstants.mode.STREAM,
  204. streamId: key
  205. });
  206. return true;
  207. }
  208. /**
  209. * Updates the internal state if the component is still mounted. This is a
  210. * workaround for all the state setting that occurs after ajax.
  211. *
  212. * @param {Object} newState - The new state to merge into the existing
  213. * state.
  214. * @private
  215. * @returns {void}
  216. */
  217. _setStateIfMounted(newState) {
  218. if (this._isMounted) {
  219. this.setState(newState);
  220. }
  221. }
  222. /**
  223. * Renders the platform specific dialog content.
  224. *
  225. * @returns {React$Component}
  226. */
  227. _renderDialogContent: () => React$Component<*>
  228. }
  229. /**
  230. * Maps part of the Redux state to the component's props.
  231. *
  232. * @param {Object} state - The Redux state.
  233. * @returns {{
  234. * _conference: Object,
  235. * _googleAPIState: number,
  236. * _googleProfileEmail: string,
  237. * _streamKey: string
  238. * }}
  239. */
  240. export function _mapStateToProps(state: Object) {
  241. return {
  242. _conference: state['features/base/conference'].conference,
  243. _googleAPIState: state['features/google-api'].googleAPIState,
  244. _googleProfileEmail: state['features/google-api'].profileEmail,
  245. _streamKey: state['features/recording'].streamKey
  246. };
  247. }