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
28 changes: 27 additions & 1 deletion src/components/MainParticipantInfo/MainParticipantInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ const useStyles = makeStyles((theme: Theme) => ({
background: '#A90000',
},
},
trackSwitchOffContainer: {
opacity: 0,
visibility: 'hidden',
transition: 'all 0.25s cubic-bezier(0.22, 0.61, 0.36, 1)',
},
isSwitchedOff: {
opacity: 1,
visibility: 'visible',
transition: 'all 0.5s linear 2s',
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure about the 2s delay. This would display a frozen video for 2 seconds before it fades to black. Seems a little long. I'm not sure I like it because it means that the UI allows the user to be confused about the frozen video for 2 seconds before we reveal what is going on. I think we might just want to let the user know right away. A fade could still be fine though.

},
switchOffMessage: {
background: '#1F304C',
borderRadius: '100px',
padding: '0.3em 1.5em',
color: '#FFFFFF',
},
}));

interface MainParticipantInfoProps {
Expand Down Expand Up @@ -173,7 +189,17 @@ export default function MainParticipantInfo({ participant, children }: MainParti
</Tooltip>
)}
</div>
{(!isVideoEnabled || isVideoSwitchedOff) && (
<div
className={clsx(classes.avatarContainer, classes.trackSwitchOffContainer, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Wondering if avatarContainer could be renamed to something more generic since it's now being used to contain something that isn't an avatar.

[classes.isSwitchedOff]: isVideoSwitchedOff,
})}
>
<Typography variant="body1" className={classes.switchOffMessage}>
Low bandwidth
</Typography>
</div>

{!isVideoEnabled && (
<div className={classes.avatarContainer}>
<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
29 changes: 28 additions & 1 deletion src/components/ParticipantInfo/ParticipantInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ const useStyles = makeStyles((theme: Theme) =>
dominantSpeaker: {
border: `solid ${borderWidth}px #7BEAA5`,
},
trackSwitchOffContainer: {
opacity: 0,
visibility: 'hidden',
transition: 'all 0.25s cubic-bezier(0.22, 0.61, 0.36, 1)',
},
isSwitchedOff: {
opacity: 1,
visibility: 'visible',
transition: 'all 0.5s linear 2s',
},
switchOffMessage: {
background: '#1F304C',
borderRadius: '100px',
padding: '0.3em 1.5em',
color: '#FFFFFF',
},
})
);

Expand Down Expand Up @@ -214,8 +230,19 @@ export default function ParticipantInfo({
</div>
<div>{isSelected && <PinIcon />}</div>
</div>

<div className={classes.innerContainer}>
{(!isVideoEnabled || isVideoSwitchedOff) && (
<div
className={clsx(classes.avatarContainer, classes.trackSwitchOffContainer, {
[classes.isSwitchedOff]: isVideoSwitchedOff,
})}
>
<Typography variant="body1" className={classes.switchOffMessage}>
Low bandwidth
</Typography>
</div>

{!isVideoEnabled && (
<div className={classes.avatarContainer}>
<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