您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

AbstractStartLiveStreamDialog.js 6.5KB

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