Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video 10788 track switch off UI #745

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ describe('the MainParticipantInfo component', () => {
expect(wrapper.find(AvatarIcon).exists()).toBe(false);
});

it('should render the AvatarIcon component when video is switched off', () => {
it('should display the Low bandwidth message when the video track is switchedOff', () => {
mockUseIsTrackSwitchedOff.mockImplementationOnce(() => true);
const wrapper = shallow(
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
);
expect(wrapper.find(AvatarIcon).exists()).toBe(true);
expect(wrapper.text()).toContain('Low bandwidth');
});

it('should not render the reconnecting UI when the user is connected', () => {
Expand Down
23 changes: 20 additions & 3 deletions src/components/MainParticipantInfo/MainParticipantInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AvatarIcon from '../../icons/AvatarIcon';
import NetworkQualityLevel from '../NetworkQualityLevel/NetworkQualityLevel';
import Tooltip from '@material-ui/core/Tooltip';
import Typography from '@material-ui/core/Typography';
import Fade from '@material-ui/core/Fade';

import useIsRecording from '../../hooks/useIsRecording/useIsRecording';
import useIsTrackSwitchedOff from '../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff';
Expand Down Expand Up @@ -58,7 +59,7 @@ const useStyles = makeStyles((theme: Theme) => ({
gridArea: '1 / 1 / 3 / 3',
},
},
avatarContainer: {
videoOffContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
Expand Down Expand Up @@ -108,6 +109,12 @@ const useStyles = makeStyles((theme: Theme) => ({
background: '#A90000',
},
},
switchOffMessage: {
background: '#1F304C',
borderRadius: '100px',
padding: '0.3em 1.5em',
color: '#FFFFFF',
},
}));

interface MainParticipantInfoProps {
Expand Down Expand Up @@ -173,8 +180,18 @@ export default function MainParticipantInfo({ participant, children }: MainParti
</Tooltip>
)}
</div>
{(!isVideoEnabled || isVideoSwitchedOff) && (
<div className={classes.avatarContainer}>
{isVideoSwitchedOff && (
<div className={classes.videoOffContainer}>
<Fade in={isVideoSwitchedOff} timeout={{ enter: 250 }}>
<Typography variant="body1" className={classes.switchOffMessage}>
Low bandwidth
</Typography>
</Fade>
</div>
)}

{!isVideoEnabled && (
<div className={classes.videoOffContainer}>
<AvatarIcon />
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ParticipantInfo/ParticipantInfo.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ describe('the ParticipantInfo component', () => {
expect(wrapper.find(AvatarIcon).exists()).toBe(false);
});

it('should render the AvatarIcon component when the video track is switchedOff', () => {
it('should display the Low bandwidth message when the video track is switchedOff', () => {
mockUseIsTrackSwitchedOff.mockImplementation(() => true);
mockUsePublications.mockImplementation(() => [{ trackName: '', kind: 'video' }]);
const wrapper = shallow(
<ParticipantInfo onClick={() => {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}>
mock children
</ParticipantInfo>
);
expect(wrapper.find(AvatarIcon).exists()).toBe(true);
expect(wrapper.text()).toContain('Low bandwidth');
});

it('should not render the reconnecting UI when the user is connected', () => {
Expand Down
24 changes: 21 additions & 3 deletions src/components/ParticipantInfo/ParticipantInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import usePublications from '../../hooks/usePublications/usePublications';
import useTrack from '../../hooks/useTrack/useTrack';
import useParticipantIsReconnecting from '../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting';
import { useAppState } from '../../state';
import { Fade } from '@material-ui/core';

const borderWidth = 2;

Expand Down Expand Up @@ -62,7 +63,7 @@ const useStyles = makeStyles((theme: Theme) =>
background: 'transparent',
top: 0,
},
avatarContainer: {
videoOffContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
Expand Down Expand Up @@ -145,6 +146,12 @@ const useStyles = makeStyles((theme: Theme) =>
dominantSpeaker: {
border: `solid ${borderWidth}px #7BEAA5`,
},
switchOffMessage: {
background: '#1F304C',
borderRadius: '100px',
padding: '0.3em 1.5em',
color: '#FFFFFF',
},
})
);

Expand Down Expand Up @@ -214,9 +221,20 @@ export default function ParticipantInfo({
</div>
<div>{isSelected && <PinIcon />}</div>
</div>

<div className={classes.innerContainer}>
{(!isVideoEnabled || isVideoSwitchedOff) && (
<div className={classes.avatarContainer}>
{isVideoSwitchedOff && (
<div className={classes.videoOffContainer}>
<Fade in={isVideoSwitchedOff} timeout={{ enter: 250 }}>
<Typography variant="body1" className={classes.switchOffMessage}>
Low bandwidth
</Typography>
</Fade>
</div>
)}

{!isVideoEnabled && (
<div className={classes.videoOffContainer}>
<AvatarIcon />
</div>
)}
Expand Down
16 changes: 7 additions & 9 deletions src/icons/AvatarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import React from 'react';

export default function AvatarIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="73" height="73" viewBox="0 0 73 73">
<g fill="none" fillRule="evenodd">
<circle cx="36.5" cy="36.5" r="36.5" fill="#FFF" />
<path
fill="#282A2B"
strokeLinejoin="round"
d="M30.973 23.925c-3.162 3.794-3.162 9.886 0 13.68l.262.301c.266.293.548.564.842.812l.192.154v2.95l-6.71 2.397-.213.082c-2.03.834-3.346 2.793-3.346 4.97v2.766c0 .532.431.963.963.963h27.874c.531 0 .962-.431.962-.963V49.27l-.005-.228-.015-.233c-.187-2.092-1.562-3.883-3.539-4.59l-6.71-2.397v-2.95l.192-.154c.393-.33.762-.702 1.104-1.112 3.162-3.795 3.162-9.887 0-13.681-3.25-3.9-8.603-3.9-11.853 0zm10.374 1.232c2.567 3.08 2.567 8.135 0 11.216-.404.484-.85.894-1.328 1.226-.26.18-.414.476-.414.791v4.111c0 .407.255.77.639.907l7.348 2.625c1.314.47 2.213 1.687 2.278 3.077l.004.183v1.781h-25.95l.002-1.804c0-1.394.843-2.65 2.131-3.18l.172-.065 7.327-2.617c.383-.137.639-.5.639-.907v-4.11c0-.316-.155-.612-.415-.792-.478-.332-.924-.742-1.328-1.226-2.567-3.08-2.567-8.135 0-11.216 2.427-2.912 6.248-2.974 8.735-.186l.16.186z"
/>
</g>
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M25.0005 50.0005C38.8076 50.0005 50.0005 38.8076 50.0005 25.0005C50.0005 11.1934 38.8076 0.000488281 25.0005 0.000488281C11.1934 0.000488281 0.000488281 11.1934 0.000488281 25.0005C0.000488281 38.8076 11.1934 50.0005 25.0005 50.0005ZM33.7505 18.7505C33.7505 23.583 29.833 27.5005 25.0005 27.5005C20.168 27.5005 16.2505 23.583 16.2505 18.7505C16.2505 13.918 20.168 10.0005 25.0005 10.0005C29.833 10.0005 33.7505 13.918 33.7505 18.7505ZM40.3855 37.7802C36.7169 42.1919 31.1867 45.0005 25.0005 45.0005C18.8143 45.0005 13.2841 42.1919 9.61551 37.7802C12.9489 34.6905 19.6001 33.0422 25.0005 33.0422C30.4009 33.0422 37.0521 34.6905 40.3855 37.7802Z"
fill="#8891AA"
/>
</svg>
);
}
12 changes: 6 additions & 6 deletions src/stories/App.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ export default {
unpublishAllAudio: {
control: { type: 'boolean' },
},
unpublishAllVideo: {
control: { type: 'boolean' },
unpublishVideo: {
control: { type: 'text' },
},
switchOffAllVideo: {
control: { type: 'boolean' },
switchOffVideo: {
control: { type: 'text' },
},
},
};
Expand All @@ -38,6 +38,6 @@ Prod.args = {
presentationParticipant: null,
disableAllAudio: false,
unpublishAllAudio: false,
unpublishAllVideo: false,
switchOffAllVideo: false,
unpublishVideo: null,
switchOffVideo: null,
};
26 changes: 16 additions & 10 deletions src/stories/mocks/twilio-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class LocalParticipant extends EventEmitter {
]);

this.identity = 'Local Participant';
this.sid = this.identity;
}
}

Expand All @@ -122,6 +123,7 @@ const mockRoom = new MockRoom();
class MockParticipant extends EventEmitter {
constructor(name) {
super();
this.sid = name;
this.identity = name;
this.tracks = new Map([
['video', new MockPublication('video')],
Expand Down Expand Up @@ -223,20 +225,24 @@ export function decorator(story, { args }) {
videoTrack?.enable();
}

if (args.unpublishAllAudio) {
mockParticipant.unpublishTrack('audio');
} else {
mockParticipant.publishTrack('audio');
}

if (args.unpublishAllVideo) {
mockParticipant.unpublishTrack('video');
if (args.unpublishVideo) {
const participantList = args.unpublishVideo.split(',');
if (participantList.includes(i.toString())) {
mockParticipant.unpublishTrack('video');
} else {
mockParticipant.publishTrack('video');
}
} else {
mockParticipant.publishTrack('video');
}

if (args.switchOffAllVideo) {
videoTrack?.switchOff();
if (args.switchOffVideo) {
const participantList = args.switchOffVideo.split(',');
if (participantList.includes(i.toString())) {
videoTrack?.switchOff();
} else {
videoTrack?.switchOn();
}
} else {
videoTrack?.switchOn();
}
Expand Down