opentok-react-native
Version:
Vonage Video client SDK for React Native
269 lines (253 loc) • 8.53 kB
JavaScript
import React from 'react';
import { Platform, View } from 'react-native';
import { ViewPropTypes } from 'deprecated-react-native-prop-types';
import PropTypes from 'prop-types';
import { isEqual } from 'underscore';
import uuid from 'react-native-uuid';
import { checkAndroidPermissions, OT } from './OT';
import OTRNPublisher from './OTPublisherNativeComponent';
import {
addEventListener,
removeEventListener,
dispatchEvent,
isConnected,
getPublisherStream,
} from './helpers/OTSessionHelper';
import { sanitizeProperties } from './helpers/OTPublisherHelper';
import OTContext from './contexts/OTContext';
export default class OTPublisher extends React.Component {
eventHandlers = {};
publisherProperties = {};
constructor(props) {
super(props);
const mergedProperties = {
...OTPublisher.defaultProps.properties,
...props.properties,
};
this.state = {
publisherId: uuid.v4(),
publishVideo: mergedProperties.publishVideo,
permissionsGranted: Platform.OS === 'ios',
componentMounted: false,
publisherProperties: sanitizeProperties(mergedProperties),
};
this.eventHandlers = props.eventHandlers;
this.initComponent(props.eventHandlers);
}
componentDidMount() {
addEventListener(
this.context.sessionId,
'sessionConnected',
this.onSessionConnected
);
this.setState({
componentMounted: true,
});
}
componentDidUpdate() {
const { properties } = this.props;
const sanitizedProperties = sanitizeProperties(properties);
if (!isEqual(this.state.publisherProperties, sanitizedProperties)) {
this.setState((prevState) => ({
publisherProperties: sanitizedProperties,
}));
}
}
onSessionConnected = () => {
if (Platform.OS === 'android') {
const { audioTrack, videoTrack, videoSource } =
this.state.publisherProperties;
const isScreenSharing = videoSource === 'screen';
checkAndroidPermissions(audioTrack, videoTrack, isScreenSharing)
.then(() => {
OT.publish(this.context.sessionId, this.state.publisherId);
this.setState({
permissionsGranted: true,
});
})
.catch((error) => {
// this.otrnEventHandler(error);
});
} else {
OT.publish(this.context.sessionId, this.state.publisherId);
}
};
initComponent = () => {
this.eventHandlers.streamCreated = this.props.eventHandlers?.streamCreated;
this.eventHandlers.streamDestroyed =
this.props.eventHandlers?.streamDestroyed;
this.eventHandlers.error = this.props.eventHandlers?.error;
this.eventHandlers.audioLevel = this.props.eventHandlers?.audioLevel;
this.eventHandlers.audioNetworkStats =
this.props.eventHandlers?.audioNetworkStats;
this.eventHandlers.rtcStatsReport =
this.props.eventHandlers?.rtcStatsReport;
this.eventHandlers.videoDisabled = this.props.eventHandlers?.videoDisabled;
this.eventHandlers.videoDisableWarning =
this.props.eventHandlers?.videoDisableWarning;
this.eventHandlers.videoDisableWarningLifted =
this.props.eventHandlers?.videoDisableWarningLifted;
this.eventHandlers.videoEnabled = this.props.eventHandlers?.videoEnabled;
this.eventHandlers.videoNetworkStats =
this.props.eventHandlers?.videoNetworkStats;
this.publisherProperties = sanitizeProperties(this.props.properties);
if (Platform.OS === 'android') {
const { audioTrack, videoTrack, videoSource } = this.publisherProperties;
const isScreenSharing = videoSource === 'screen';
checkAndroidPermissions(audioTrack, videoTrack, isScreenSharing)
.then(() => {
if (this.context && isConnected(this.context.sessionId)) {
setTimeout(
() => OT.publish(this.context.sessionId, this.state.publisherId),
0
);
}
this.setState({
permissionsGranted: true,
});
})
.catch((error) => {
// this.otrnEventHandler(error);
});
} else {
// Context and publisherId might not be available immediately
// So we delay the publish call slightly
setTimeout(() => {
if (this.context && isConnected(this.context.sessionId)) {
OT.publish(this.context.sessionId, this.state.publisherId);
}
}, 100);
}
};
getRtcStatsReport() {
//NOSONAR - this method is exposed externally
OT.getPublisherRtcStatsReport(
this.context.sessionId,
this.state.publisherId
);
}
componentWillUnmount() {
OT.unpublish(this.context.sessionId, this.state.publisherId);
const publisherStreamId = getPublisherStream(this.context.sessionId);
if (publisherStreamId) {
const event = {
streamId: publisherStreamId,
nativeEvent: {
streamId: publisherStreamId,
},
};
this.onStreamDestroyed(event);
}
removeEventListener(
this.context.sessionId,
'sessionConnected',
this.onSessionConnected
);
}
getPrePermissionViewStyle = () => ({
backgroundColor: '#000',
...this.props.style,
});
onStreamDestroyed = (event) => {
dispatchEvent(this.context.sessionId, 'publisherStreamDestroyed', event);
this.props.eventHandlers?.streamDestroyed?.(event.nativeEvent);
};
render() {
return this.state.permissionsGranted && this.state.componentMounted ? (
<OTRNPublisher
sessionId={this.context.sessionId}
publisherId={this.state.publisherId}
onError={(event) => {
this.props.eventHandlers?.error?.(event.nativeEvent);
}}
onStreamCreated={(event) => {
dispatchEvent(
this.context.sessionId,
'publisherStreamCreated',
event.nativeEvent
);
this.props.eventHandlers?.streamCreated?.(event.nativeEvent);
}}
onStreamDestroyed={this.onStreamDestroyed}
onAudioLevel={(event) => {
this.props.eventHandlers?.audioLevel?.(event.nativeEvent);
}}
onAudioNetworkStats={(event) => {
// TODO - remove workaround for Android stats prop
const eventData = event.nativeEvent.jsonStats
? JSON.parse(event.nativeEvent.jsonStats)
: event.nativeEvent.stats;
this.props.eventHandlers?.audioNetworkStats?.(eventData);
}}
onRtcStatsReport={(event) => {
this.props.eventHandlers?.rtcStatsReport?.(
JSON.parse(event.nativeEvent.jsonStats)
);
}}
onVideoDisabled={(event) => {
this.props.eventHandlers?.videoDisabled?.(event.nativeEvent);
}}
onVideoDisableWarning={(event) => {
this.props.eventHandlers?.videoDisableWarning?.(event.nativeEvent);
}}
onVideoDisableWarningLifted={(event) => {
this.props.eventHandlers?.videoDisableWarningLifted?.(
event.nativeEvent
);
}}
onVideoEnabled={(event) => {
this.props.eventHandlers?.videoEnabled?.(event.nativeEvent);
}}
onVideoNetworkStats={(event) => {
// TODO - remove workaround for Android stats prop
const eventData = event.nativeEvent.jsonStats
? JSON.parse(event.nativeEvent.jsonStats)
: event.nativeEvent.stats;
this.props.eventHandlers?.videoNetworkStats?.(eventData);
}}
style={this.props.style}
{...this.state.publisherProperties}
/>
) : (
<View style={this.getPrePermissionViewStyle()} />
);
}
}
OTPublisher.propTypes = {
eventHandlers: PropTypes.object,
properties: PropTypes.object,
style: ViewPropTypes.style,
};
OTPublisher.defaultProps = {
eventHandlers: {},
properties: {
publishAudio: true,
publishVideo: true,
audioBitrate: 40000,
audioFallback: {
publisher: false,
subscriber: true,
},
audioTrack: true,
cameraPosition: 'front',
enableDtx: false,
frameRate: 30,
name: '',
publishCaptions: false,
scalableScreenshare: false,
allowAudioCaptureWhileMuted: false,
publishSenderStats: false,
resolution: 'MEDIUM',
videoTrack: true,
videoSource: 'camera',
videoContentHint: '',
maxVideoBitrate: 0,
videoBitratePreset: 'default',
scaleBehavior: 'fill',
preferredVideoCodecs: '',
},
style: {
flex: 1,
},
};
OTPublisher.contextType = OTContext;