Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/components/MainParticipant/MainParticipant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import MainParticipantInfo from '../MainParticipantInfo/MainParticipantInfo';
import ParticipantTracks from '../ParticipantTracks/ParticipantTracks';
import React from 'react';
import useMainSpeaker from '../../hooks/useMainSpeaker/useMainSpeaker';

export default function MainParticipant() {
const mainParticipant = useMainSpeaker();
return (
/* audio is disabled for this participant component because this participant's audio
is already being rendered in the <ParticipantStrip /> component. */
<MainParticipantInfo participant={mainParticipant}>
<ParticipantTracks participant={mainParticipant} disableAudio />
</MainParticipantInfo>
);
}
46 changes: 46 additions & 0 deletions src/components/MainParticipantInfo/MainParticipantInfo.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import MainParticipantInfo from './MainParticipantInfo';
import { shallow } from 'enzyme';
import usePublications from '../../hooks/usePublications/usePublications';

jest.mock('../../hooks/useParticipantNetworkQualityLevel/useParticipantNetworkQualityLevel', () => () => 4);

jest.mock('../../hooks/usePublications/usePublications');

const mockUsePublications = usePublications as jest.Mock<any>;

describe('the MainParticipantInfo component', () => {
it('should render correctly', () => {
mockUsePublications.mockImplementation(() => []);
const wrapper = shallow(
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
);
expect(wrapper).toMatchSnapshot();
});

it('should add hideVideoProp to InfoContainer component when video is disabled', () => {
mockUsePublications.mockImplementation(() => [{ trackName: 'camera', isTrackEnabled: false }]);
const wrapper = shallow(
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
);
expect(
wrapper
.find('Styled(div)')
.at(1)
.prop('hideVideo')
).toEqual(true);
});

it('should not add hideVideoProp to InfoContainer component when video is enabled', () => {
mockUsePublications.mockImplementation(() => [{ trackName: 'camera', isTrackEnabled: true }]);
const wrapper = shallow(
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
);
expect(
wrapper
.find('Styled(div)')
.at(1)
.prop('hideVideo')
).toEqual(false);
});
});
47 changes: 47 additions & 0 deletions src/components/MainParticipantInfo/MainParticipantInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { styled } from '@material-ui/core/styles';
import { LocalParticipant, RemoteParticipant } from 'twilio-video';
import usePublications from '../../hooks/usePublications/usePublications';
import usePublicationIsTrackEnabled from '../../hooks/usePublicationIsTrackEnabled/usePublicationIsTrackEnabled';

const Container = styled('div')({
position: 'relative',
display: 'flex',
alignItems: 'center',
});

const InfoContainer = styled('div')({
position: 'absolute',
zIndex: 1,
height: '100%',
padding: '0.4em',
width: '100%',
background: ({ hideVideo }: { hideVideo?: boolean }) => (hideVideo ? 'black' : 'transparent'),
});

const Identity = styled('h4')({
background: 'rgba(0, 0, 0, 0.7)',
padding: '0.1em 0.3em',
margin: '1em',
fontSize: '1.2em',
display: 'inline-block',
});

interface ParticipantInfoProps {
participant: LocalParticipant | RemoteParticipant;
children: React.ReactNode;
}

export default function ParticipantInfo({ participant, children }: ParticipantInfoProps) {
const publications = usePublications(participant);
const isVideoEnabled = usePublicationIsTrackEnabled(publications.find(p => p.trackName === 'camera'));

return (
<Container>
<InfoContainer hideVideo={!isVideoEnabled}>
<Identity>{participant.identity}</Identity>
</InfoContainer>
{children}
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`the MainParticipantInfo component should render correctly 1`] = `
<Styled(div)>
<Styled(div)
hideVideo={true}
>
<Styled(h4)>
mockIdentity
</Styled(h4)>
</Styled(div)>
mock children
</Styled(div)>
`;
19 changes: 0 additions & 19 deletions src/components/Menu/Menu.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import Menu from './Menu';
import { getToken, receiveToken } from '../../store/main/main';
import useRoomState from '../../hooks/useRoomState/useRoomState';
import { IVideoContext, useVideoContext } from '../../hooks/context';
import { fireEvent, render } from '@testing-library/react';
Expand Down Expand Up @@ -64,22 +63,4 @@ describe('the Menu component', () => {
fireEvent.change(getByLabelText('Room'), { target: { value: 'Foo' } });
expect(getByRole('button').getAttribute('disabled')).toEqual('');
});

it('should dispatch a redux action when the Join Room button is clicked', () => {
mockedUseRoomState.mockImplementation(() => 'disconnected');
mockedUseVideoContext.mockImplementation(() => ({ isConnecting: false } as any));
const { getByLabelText, getByRole } = render(<Menu />);
fireEvent.change(getByLabelText('Name'), { target: { value: 'Username' } });
fireEvent.change(getByLabelText('Room'), { target: { value: 'Roomname' } });
fireEvent.click(getByRole('button'));
expect(getToken).toHaveBeenCalledWith('Username', 'Roomname');
});

it('should dispatch a redux action when the Leave Room button is clicked', () => {
mockedUseRoomState.mockImplementation(() => 'connected');
mockedUseVideoContext.mockImplementation(() => ({ isConnecting: false } as any));
const { getByRole } = render(<Menu />);
fireEvent.click(getByRole('button'));
expect(receiveToken).toHaveBeenCalledWith('');
});
});
9 changes: 6 additions & 3 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CircularProgress from '@material-ui/core/CircularProgress';
import TextField from '@material-ui/core/TextField';
import Toolbar from '@material-ui/core/Toolbar';

import { getToken, receiveToken } from '../../store/main/main';
import { getToken } from '../../store/main/main';
import useRoomState from '../../hooks/useRoomState/useRoomState';
import { useVideoContext } from '../../hooks/context';

Expand All @@ -26,6 +26,9 @@ const useStyles = makeStyles((theme: Theme) =>
marginRight: theme.spacing(1),
width: 200,
},
loadingSpinner: {
marginLeft: '1em',
},
})
);

Expand Down Expand Up @@ -75,10 +78,10 @@ export default function Menu() {
<Button type="submit" color="primary" variant="contained" disabled={isConnecting || !name || !roomName}>
Join Room
</Button>
{isConnecting && <CircularProgress></CircularProgress>}
{isConnecting && <CircularProgress className={classes.loadingSpinner} />}
</form>
) : (
<Button onClick={() => dispatch(receiveToken(''))}>Leave Room</Button>
<h3>{roomName}</h3>
)}
</Toolbar>
</AppBar>
Expand Down
1 change: 1 addition & 0 deletions src/components/ParticipantStrip/ParticipantStrip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Container = styled('aside')(({ theme }) => ({
right: `calc(100% - ${theme.sidebarPosition})`,
left: 0,
padding: '0.5em',
overflowY: 'auto',
}));

export default function ParticipantStrip() {
Expand Down
8 changes: 2 additions & 6 deletions src/components/Room/Room.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react';
import Participant from '../Participant/Participant';
import ParticipantStrip from '../ParticipantStrip/ParticipantStrip';
import { styled } from '@material-ui/core/styles';
import useMainSpeaker from '../../hooks/useMainSpeaker/useMainSpeaker';
import MainParticipant from '../MainParticipant/MainParticipant';

const Container = styled('div')({
position: 'relative',
Expand All @@ -21,14 +20,11 @@ const MainParticipantContainer = styled('div')(({ theme }) => ({
}));

export default function Room() {
const mainParticipant = useMainSpeaker();
return (
<Container>
<ParticipantStrip />
<MainParticipantContainer>
{/* audio is disabled for this participant component because this participant's audio
is already being rendered in the <ParticipantStrip /> component. */}
<Participant participant={mainParticipant} disableAudio enableScreenShare />
<MainParticipant />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

</MainParticipantContainer>
</Container>
);
Expand Down