Skip to content

Commit

Permalink
rn: add youtube player for mobile app
Browse files Browse the repository at this point in the history
  • Loading branch information
tmoldovan8x8 authored Jun 12, 2020
1 parent 8758c22 commit df64dd8
Show file tree
Hide file tree
Showing 21 changed files with 923 additions and 17 deletions.
5 changes: 4 additions & 1 deletion conference.js
Original file line number Diff line number Diff line change
Expand Up @@ -2517,14 +2517,17 @@ export default {
if (state === 'stop'
|| state === 'start'
|| state === 'playing') {
const localParticipant = getLocalParticipant(APP.store.getState());

room.removeCommand(this.commands.defaults.SHARED_VIDEO);
room.sendCommandOnce(this.commands.defaults.SHARED_VIDEO, {
value: url,
attributes: {
state,
time,
muted: isMuted,
volume
volume,
from: localParticipant.id
}
});
} else {
Expand Down
11 changes: 9 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"react-native-watch-connectivity": "0.4.3",
"react-native-webrtc": "1.75.3",
"react-native-webview": "7.4.1",
"react-native-youtube-iframe": "1.2.3",
"react-redux": "7.1.0",
"react-textarea-autosize": "7.1.0",
"react-transition-group": "2.4.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { Component } from 'react';
import { Text, View } from 'react-native';

import { YoutubeLargeVideo } from '../../../youtube-player';
import { Avatar } from '../../avatar';
import { translate } from '../../i18n';
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
Expand All @@ -15,7 +16,7 @@ import { connect } from '../../redux';
import type { StyleType } from '../../styles';
import { TestHint } from '../../testing/components';
import { getTrackByMediaTypeAndParticipant } from '../../tracks';
import { shouldRenderParticipantVideo } from '../functions';
import { shouldRenderParticipantVideo, getParticipantById } from '../functions';

import styles from './styles';

Expand All @@ -33,6 +34,13 @@ type Props = {
*/
_connectionStatus: string,

/**
* True if the participant which this component represents is fake.
*
* @private
*/
_isFakeParticipant: boolean,

/**
* The name of the participant which this component represents.
*
Expand Down Expand Up @@ -181,8 +189,10 @@ class ParticipantView extends Component<Props> {
render() {
const {
_connectionStatus: connectionStatus,
_isFakeParticipant,
_renderVideo: renderVideo,
_videoTrack: videoTrack,
disableVideo,
onPress,
tintStyle
} = this.props;
Expand All @@ -198,9 +208,11 @@ class ParticipantView extends Component<Props> {
? this.props.testHintId
: `org.jitsi.meet.Participant#${this.props.participantId}`;

const renderYoutubeLargeVideo = _isFakeParticipant && !disableVideo;

return (
<Container
onClick = { renderVideo ? undefined : onPress }
onClick = { renderVideo || renderYoutubeLargeVideo ? undefined : onPress }
style = {{
...styles.participantView,
...this.props.style
Expand All @@ -209,18 +221,20 @@ class ParticipantView extends Component<Props> {

<TestHint
id = { testHintId }
onPress = { onPress }
onPress = { renderYoutubeLargeVideo ? undefined : onPress }
value = '' />

{ renderVideo
{ renderYoutubeLargeVideo && <YoutubeLargeVideo youtubeId = { this.props.participantId } /> }

{ !_isFakeParticipant && renderVideo
&& <VideoTrack
onPress = { onPress }
videoTrack = { videoTrack }
waitForVideoStarted = { false }
zOrder = { this.props.zOrder }
zoomEnabled = { this.props.zoomEnabled } /> }

{ !renderVideo
{ !renderYoutubeLargeVideo && !renderVideo
&& <View style = { styles.avatarContainer }>
<Avatar
participantId = { this.props.participantId }
Expand Down Expand Up @@ -253,13 +267,15 @@ class ParticipantView extends Component<Props> {
*/
function _mapStateToProps(state, ownProps) {
const { disableVideo, participantId } = ownProps;
const participant = getParticipantById(state, participantId);
let connectionStatus;
let participantName;

return {
_connectionStatus:
connectionStatus
|| JitsiParticipantConnectionStatus.ACTIVE,
_isFakeParticipant: participant && participant.isFakeParticipant,
_participantName: participantName,
_renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
_videoTrack:
Expand Down
17 changes: 17 additions & 0 deletions react/features/base/participants/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,23 @@ function _getAllParticipants(stateful) {
: toState(stateful)['features/base/participants'] || []);
}

/**
* Returns the youtube fake participant.
* At the moment it is considered the youtube participant the only fake participant in the list.
*
* @param {(Function|Object|Participant[])} stateful - The redux state
* features/base/participants, the (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/participants.
* @private
* @returns {Participant}
*/
export function getYoutubeParticipant(stateful: Object | Function) {
const participants = _getAllParticipants(stateful);

return participants.filter(p => p.isFakeParticipant)[0];
}

/**
* Returns true if all of the meeting participants are moderators.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Text, View } from 'react-native';

import {
getLocalParticipant,
getParticipantById,
getParticipantDisplayName,
shouldRenderParticipantVideo
} from '../../../base/participants';
Expand Down Expand Up @@ -65,13 +66,16 @@ class DisplayNameLabel extends Component<Props> {
function _mapStateToProps(state: Object, ownProps: Props) {
const { participantId } = ownProps;
const localParticipant = getLocalParticipant(state);
const participant = getParticipantById(state, participantId);
const isFakeParticipant = participant && participant.isFakeParticipant;

// Currently we only render the display name if it's not the local
// participant and there is no video rendered for
// them.
const _render = Boolean(participantId)
&& localParticipant.id !== participantId
&& !shouldRenderParticipantVideo(state, participantId);
&& !shouldRenderParticipantVideo(state, participantId)
&& !isFakeParticipant;

return {
_participantName:
Expand Down
16 changes: 8 additions & 8 deletions react/features/filmstrip/components/native/Thumbnail.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function Thumbnail(props: Props) {

<ParticipantView
avatarSize = { AVATAR_SIZE }
disableVideo = { isScreenShare }
disableVideo = { isScreenShare || participant.isFakeParticipant }
participantId = { participantId }
style = { _styles.participantViewStyle }
tintEnabled = { participantInLargeVideo && !disableTint }
Expand All @@ -162,32 +162,32 @@ function Thumbnail(props: Props) {
{ renderModeratorIndicator
&& <View style = { styles.moderatorIndicatorContainer }>
<ModeratorIndicator />
</View> }
</View>}

<View
{ !participant.isFakeParticipant && <View
style = { [
styles.thumbnailTopIndicatorContainer,
styles.thumbnailTopLeftIndicatorContainer
] }>
<RaisedHandIndicator participantId = { participant.id } />
{ renderDominantSpeakerIndicator && <DominantSpeakerIndicator /> }
</View>
</View> }

<View
{ !participant.isFakeParticipant && <View
style = { [
styles.thumbnailTopIndicatorContainer,
styles.thumbnailTopRightIndicatorContainer
] }>
<ConnectionIndicator participantId = { participant.id } />
</View>
</View> }

<Container style = { styles.thumbnailIndicatorContainer }>
{ !participant.isFakeParticipant && <Container style = { styles.thumbnailIndicatorContainer }>
{ audioMuted
&& <AudioMutedIndicator /> }

{ videoMuted
&& <VideoMutedIndicator /> }
</Container>
</Container> }

</Container>
);
Expand Down
2 changes: 2 additions & 0 deletions react/features/toolbox/components/native/OverflowMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { LiveStreamButton, RecordButton } from '../../../recording';
import { RoomLockButton } from '../../../room-lock';
import { ClosedCaptionButton } from '../../../subtitles';
import { TileViewButton } from '../../../video-layout';
import { VideoShareButton } from '../../../youtube-player';
import HelpButton from '../HelpButton';

import AudioOnlyButton from './AudioOnlyButton';
Expand Down Expand Up @@ -136,6 +137,7 @@ class OverflowMenu extends PureComponent<Props, State> {
<TileViewButton { ...buttonProps } />
<RecordButton { ...buttonProps } />
<LiveStreamButton { ...buttonProps } />
<VideoShareButton { ...buttonProps } />
<RoomLockButton { ...buttonProps } />
<ClosedCaptionButton { ...buttonProps } />
<SharedDocumentButton { ...buttonProps } />
Expand Down
22 changes: 22 additions & 0 deletions react/features/youtube-player/actionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* The type of the action which signals to update the current known state of the
* shared YouTube video.
*
* {
* type: SET_SHARED_VIDEO_STATUS,
* status: string,
* time: string,
* ownerId: string
* }
*/
export const SET_SHARED_VIDEO_STATUS = 'SET_SHARED_VIDEO_STATUS';

/**
* The type of the action which signals to start the flow for starting or
* stopping a shared YouTube video.
*
* {
* type: TOGGLE_SHARED_VIDEO
* }
*/
export const TOGGLE_SHARED_VIDEO = 'TOGGLE_SHARED_VIDEO';
54 changes: 54 additions & 0 deletions react/features/youtube-player/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @flow

import { openDialog } from '../base/dialog';

import { SET_SHARED_VIDEO_STATUS } from './actionTypes';
import { EnterVideoLinkPrompt } from './components';

/**
* Updates the current known status of the shared YouTube video.
*
* @param {string} videoId - The youtubeId of the video to be shared.
* @param {string} status - The current status of the YouTube video being shared.
* @param {number} time - The current position of the YouTube video being shared.
* @param {string} ownerId - The participantId of the user sharing the YouTube video.
* @returns {{
* type: SET_SHARED_VIDEO_STATUS,
* ownerId: string,
* status: string,
* time: number,
* videoId: string
* }}
*/
export function setSharedVideoStatus(videoId: string, status: string, time: number, ownerId: string) {
return {
type: SET_SHARED_VIDEO_STATUS,
ownerId,
status,
time,
videoId
};
}

/**
* Starts the flow for starting or stopping a shared YouTube video.
*
* @returns {{
* type: TOGGLE_SHARED_VIDEO
* }}
*/
export function toggleSharedVideo() {
return {
type: 'TOGGLE_SHARED_VIDEO'
};
}

/**
* Displays the prompt for entering the youtube video link.
*
* @param {Function} onPostSubmit - The function to be invoked when a valid link is entered.
* @returns {Function}
*/
export function showEnterVideoLinkPrompt(onPostSubmit: ?Function) {
return openDialog(EnterVideoLinkPrompt, { onPostSubmit });
}
Loading

0 comments on commit df64dd8

Please sign in to comment.