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

AbstractStartLiveStreamDialog.ts 6.7KB

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