jitsi-meet/react/features/recording/components/LiveStream/AbstractStartLiveStreamDialog.ts
theluyuan 38ba663466
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
init
2025-09-02 14:49:16 +08:00

252 lines
6.7 KiB
TypeScript

import { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { createLiveStreamingDialogEvent } from '../../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../analytics/functions';
import { IReduxState, IStore } from '../../../app/types';
import { IJitsiConference } from '../../../base/conference/reducer';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
/**
* The type of the React {@code Component} props of
* {@link AbstractStartLiveStreamDialog}.
*/
export interface IProps extends WithTranslation {
/**
* The {@code JitsiConference} for the current conference.
*/
_conference?: IJitsiConference;
/**
* The current state of interactions with the Google API. Determines what
* Google related UI should display.
*/
_googleAPIState: number;
/**
* The email of the user currently logged in to the Google web client
* application.
*/
_googleProfileEmail: string;
/**
* The live stream key that was used before.
*/
_streamKey?: string;
/**
* The Redux dispatch function.
*/
dispatch: IStore['dispatch'];
navigation?: any;
}
/**
* The type of the React {@code Component} state of
* {@link AbstractStartLiveStreamDialog}.
*/
export interface IState {
/**
* Details about the broadcasts available for use for the logged in Google
* user's YouTube account.
*/
broadcasts?: Array<any>;
/**
* The error type, as provided by Google, for the most recent error
* encountered by the Google API.
*/
errorType?: string;
/**
* The boundStreamID of the broadcast currently selected in the broadcast
* dropdown.
*/
selectedBoundStreamID?: string;
/**
* The selected or entered stream key to use for YouTube live streaming.
*/
streamKey?: string;
}
/**
* Implements an abstract class for the StartLiveStreamDialog on both platforms.
*
* NOTE: Google log-in is not supported for mobile yet for later implementation
* but the abstraction of its properties are already present in this abstract
* class.
*/
export default class AbstractStartLiveStreamDialog<P extends IProps>
extends Component<P, IState> {
_isMounted: boolean;
/**
* Constructor of the component.
*
* @inheritdoc
*/
constructor(props: P) {
super(props);
this.state = {
broadcasts: undefined,
errorType: undefined,
selectedBoundStreamID: undefined,
streamKey: ''
};
/**
* Instance variable used to flag whether the component is or is not
* mounted. Used as a hack to avoid setting state on an unmounted
* component.
*
* @private
* @type {boolean}
*/
this._isMounted = false;
this._onCancel = this._onCancel.bind(this);
this._onStreamKeyChange = this._onStreamKeyChange.bind(this);
this._onSubmit = this._onSubmit.bind(this);
}
/**
* Implements {@link Component#componentDidMount()}. Invoked immediately
* after this component is mounted.
*
* @inheritdoc
* @returns {void}
*/
override componentDidMount() {
this._isMounted = true;
}
/**
* Implements React's {@link Component#componentWillUnmount()}. Invoked
* immediately before this component is unmounted and destroyed.
*
* @inheritdoc
*/
override componentWillUnmount() {
this._isMounted = false;
}
/**
* Invokes the passed in {@link onCancel} callback and closes
* {@code StartLiveStreamDialog}.
*
* @private
* @returns {boolean} True is returned to close the modal.
*/
_onCancel() {
sendAnalytics(createLiveStreamingDialogEvent('start', 'cancel.button'));
return true;
}
/**
* Asks the user to sign in, if not already signed in, and then requests a
* list of the user's YouTube broadcasts.
*
* NOTE: To be implemented by platforms.
*
* @private
* @returns {Promise}
*/
_onGetYouTubeBroadcasts(): Promise<any> | void {
// to be overwritten by child classes.
}
/**
* Callback invoked to update the {@code StartLiveStreamDialog} component's
* display of the entered YouTube stream key.
*
* @param {string} streamKey - The stream key entered in the field.
* @private
* @returns {void}
*/
_onStreamKeyChange(streamKey: string) {
this._setStateIfMounted({
streamKey,
selectedBoundStreamID: undefined
});
}
/**
* Invokes the passed in {@link onSubmit} callback with the entered stream
* key, and then closes {@code StartLiveStreamDialog}.
*
* @private
* @returns {boolean} False if no stream key is entered to preventing
* closing, true to close the modal.
*/
_onSubmit() {
const { broadcasts, selectedBoundStreamID } = this.state;
const key
= (this.state.streamKey || this.props._streamKey || '').trim();
if (!key) {
return false;
}
let selectedBroadcastID = null;
if (selectedBoundStreamID) {
const selectedBroadcast = broadcasts?.find(
broadcast => broadcast.boundStreamID === selectedBoundStreamID);
selectedBroadcastID = selectedBroadcast?.id;
}
sendAnalytics(
createLiveStreamingDialogEvent('start', 'confirm.button'));
this.props._conference?.startRecording({
broadcastId: selectedBroadcastID,
mode: JitsiRecordingConstants.mode.STREAM,
streamId: key
});
return true;
}
/**
* Updates the internal state if the component is still mounted. This is a
* workaround for all the state setting that occurs after ajax.
*
* @param {Object} newState - The new state to merge into the existing
* state.
* @private
* @returns {void}
*/
_setStateIfMounted(newState: IState) {
if (this._isMounted) {
this.setState(newState);
}
}
}
/**
* Maps part of the Redux state to the component's props.
*
* @param {Object} state - The Redux state.
* @returns {{
* _conference: Object,
* _googleAPIState: number,
* _googleProfileEmail: string,
* _streamKey: string
* }}
*/
export function _mapStateToProps(state: IReduxState) {
return {
_conference: state['features/base/conference'].conference,
_googleAPIState: state['features/google-api'].googleAPIState,
_googleProfileEmail: state['features/google-api'].profileEmail,
_streamKey: state['features/recording'].streamKey
};
}