diff --git a/src/components/MainParticipant/MainParticipant.tsx b/src/components/MainParticipant/MainParticipant.tsx
new file mode 100644
index 000000000..f5275cd8d
--- /dev/null
+++ b/src/components/MainParticipant/MainParticipant.tsx
@@ -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 component. */
+
+
+
+ );
+}
diff --git a/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx b/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx
new file mode 100644
index 000000000..56718a580
--- /dev/null
+++ b/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx
@@ -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;
+
+describe('the MainParticipantInfo component', () => {
+ it('should render correctly', () => {
+ mockUsePublications.mockImplementation(() => []);
+ const wrapper = shallow(
+ mock children
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it('should add hideVideoProp to InfoContainer component when video is disabled', () => {
+ mockUsePublications.mockImplementation(() => [{ trackName: 'camera', isTrackEnabled: false }]);
+ const wrapper = shallow(
+ mock children
+ );
+ 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(
+ mock children
+ );
+ expect(
+ wrapper
+ .find('Styled(div)')
+ .at(1)
+ .prop('hideVideo')
+ ).toEqual(false);
+ });
+});
diff --git a/src/components/MainParticipantInfo/MainParticipantInfo.tsx b/src/components/MainParticipantInfo/MainParticipantInfo.tsx
new file mode 100644
index 000000000..4d2a9485e
--- /dev/null
+++ b/src/components/MainParticipantInfo/MainParticipantInfo.tsx
@@ -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 (
+
+
+ {participant.identity}
+
+ {children}
+
+ );
+}
diff --git a/src/components/MainParticipantInfo/__snapshots__/MainParticipantInfo.test.tsx.snap b/src/components/MainParticipantInfo/__snapshots__/MainParticipantInfo.test.tsx.snap
new file mode 100644
index 000000000..311a159de
--- /dev/null
+++ b/src/components/MainParticipantInfo/__snapshots__/MainParticipantInfo.test.tsx.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`the MainParticipantInfo component should render correctly 1`] = `
+
+
+
+ mockIdentity
+
+
+ mock children
+
+`;
diff --git a/src/components/Menu/Menu.test.tsx b/src/components/Menu/Menu.test.tsx
index b822a5552..6e06552a4 100644
--- a/src/components/Menu/Menu.test.tsx
+++ b/src/components/Menu/Menu.test.tsx
@@ -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';
@@ -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();
- 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();
- fireEvent.click(getByRole('button'));
- expect(receiveToken).toHaveBeenCalledWith('');
- });
});
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index b1beaa53f..9a1059d81 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -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';
@@ -26,6 +26,9 @@ const useStyles = makeStyles((theme: Theme) =>
marginRight: theme.spacing(1),
width: 200,
},
+ loadingSpinner: {
+ marginLeft: '1em',
+ },
})
);
@@ -75,10 +78,10 @@ export default function Menu() {
- {isConnecting && }
+ {isConnecting && }
) : (
-
+ {roomName}
)}
diff --git a/src/components/ParticipantStrip/ParticipantStrip.tsx b/src/components/ParticipantStrip/ParticipantStrip.tsx
index 8ae2db606..a93da73b3 100644
--- a/src/components/ParticipantStrip/ParticipantStrip.tsx
+++ b/src/components/ParticipantStrip/ParticipantStrip.tsx
@@ -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() {
diff --git a/src/components/Room/Room.tsx b/src/components/Room/Room.tsx
index 54bf43019..632201aa1 100644
--- a/src/components/Room/Room.tsx
+++ b/src/components/Room/Room.tsx
@@ -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',
@@ -21,14 +20,11 @@ const MainParticipantContainer = styled('div')(({ theme }) => ({
}));
export default function Room() {
- const mainParticipant = useMainSpeaker();
return (
- {/* audio is disabled for this participant component because this participant's audio
- is already being rendered in the component. */}
-
+
);