Merge pull request #6486 from SimonBrandner/feature/muting

Show an avatar/a turned off microphone icon for muted users
This commit is contained in:
David Baker 2021-08-02 17:20:55 +01:00 committed by GitHub
commit 3ea0571c5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 31 deletions

View file

@ -76,16 +76,22 @@ limitations under the License.
&.mx_VideoFeed_voice {
// We don't want to collide with the call controls that have 52px of height
padding-bottom: 52px;
margin-bottom: 52px;
background-color: $inverted-bg-color;
display: flex;
justify-content: center;
align-items: center;
}
&.mx_VideoFeed_video {
.mx_VideoFeed_video {
height: 100%;
background-color: #000;
}
.mx_VideoFeed_mic {
left: 10px;
bottom: 10px;
}
}
}

View file

@ -35,12 +35,23 @@ limitations under the License.
width: 100%;
&.mx_VideoFeed_voice {
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
aspect-ratio: 16 / 9;
}
.mx_VideoFeed_video {
border-radius: 4px;
}
.mx_VideoFeed_mic {
left: 6px;
bottom: 6px;
}
}
&.mx_CallViewSidebar_pipMode {

View file

@ -15,18 +15,52 @@ limitations under the License.
*/
.mx_VideoFeed {
border-radius: 4px;
overflow: hidden;
position: relative;
&.mx_VideoFeed_voice {
background-color: $inverted-bg-color;
}
&.mx_VideoFeed_video {
.mx_VideoFeed_video {
width: 100%;
background-color: transparent;
&.mx_VideoFeed_video_mirror {
transform: scale(-1, 1);
}
}
.mx_VideoFeed_mic {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
background-color: rgba(0, 0, 0, 0.5); // Same on both themes
border-radius: 100%;
&::before {
position: absolute;
content: "";
width: 16px;
height: 16px;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
background-color: white; // Same on both themes
border-radius: 7px;
}
&.mx_VideoFeed_mic_muted::before {
mask-image: url('$(res)/img/voip/mic-muted.svg');
}
&.mx_VideoFeed_mic_unmuted::before {
mask-image: url('$(res)/img/voip/mic-unmuted.svg');
}
}
}
.mx_VideoFeed_mirror {
transform: scale(-1, 1);
}

View file

@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.9206 1.0544C1.68141 0.815201 1.29359 0.815201 1.0544 1.0544C0.815201 1.29359 0.815201 1.68141 1.0544 1.9206L4.55 5.41621V7C4.55 8.3531 5.6469 9.45 7 9.45C7.45436 9.45 7.87983 9.32632 8.24458 9.11079L9.12938 9.99558C8.52863 10.4234 7.7937 10.675 7 10.675C4.97035 10.675 3.325 9.02965 3.325 7C3.325 6.66173 3.05077 6.3875 2.7125 6.3875C2.37423 6.3875 2.1 6.66173 2.1 7C2.1 9.49877 3.97038 11.5607 6.3875 11.8621V12.5125C6.3875 12.8508 6.66173 13.125 7 13.125C7.33827 13.125 7.6125 12.8508 7.6125 12.5125V11.8621C8.50718 11.7505 9.32696 11.3978 10.0047 10.8709L12.0794 12.9456C12.3186 13.1848 12.7064 13.1848 12.9456 12.9456C13.1848 12.7064 13.1848 12.3186 12.9456 12.0794L1.9206 1.0544Z" fill="white"/>
<path d="M10.5474 7.96338L11.5073 8.92525C11.7601 8.33424 11.9 7.68346 11.9 7C11.9 6.66173 11.6258 6.3875 11.2875 6.3875C10.9492 6.3875 10.675 6.66173 10.675 7C10.675 7.33336 10.6306 7.65634 10.5474 7.96338Z" fill="white"/>
<path d="M4.81385 2.21784L9.45 6.86366V3.325C9.45 1.9719 8.3531 0.875 7 0.875C6.04532 0.875 5.21818 1.42104 4.81385 2.21784Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.4645 3.29384C4.4645 1.95795 5.59973 0.875 7.0001 0.875C8.40048 0.875 9.53571 1.95795 9.53571 3.29384V6.91127C9.53571 8.24716 8.40048 9.33011 7.0001 9.33011C5.59973 9.33011 4.4645 8.24716 4.4645 6.91127V3.29384Z" fill="white"/>
<path d="M2.56269 6.1391C3.01153 6.1391 3.37539 6.4862 3.37539 6.91437C3.37539 8.81701 4.99198 10.3617 6.99032 10.3666C6.99359 10.3666 6.99686 10.3666 7.00014 10.3666C7.0034 10.3666 7.00665 10.3666 7.0099 10.3666C9.00814 10.3616 10.6246 8.81694 10.6246 6.91437C10.6246 6.4862 10.9885 6.1391 11.4373 6.1391C11.8861 6.1391 12.25 6.4862 12.25 6.91437C12.25 9.41469 10.3257 11.4854 7.81283 11.8576V12.3497C7.81283 12.7779 7.44898 13.125 7.00014 13.125C6.5513 13.125 6.18744 12.7779 6.18744 12.3497V11.8576C3.67448 11.4855 1.75 9.41478 1.75 6.91437C1.75 6.4862 2.11386 6.1391 2.56269 6.1391Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 945 B

View file

@ -22,6 +22,7 @@ import { CallFeed, CallFeedEvent } from 'matrix-js-sdk/src/webrtc/callFeed';
import { logger } from 'matrix-js-sdk/src/logger';
import MemberAvatar from "../avatars/MemberAvatar";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { SDPStreamMetadataPurpose } from 'matrix-js-sdk/src/webrtc/callEventTypes';
interface IProps {
call: MatrixCall;
@ -47,7 +48,7 @@ interface IState {
}
@replaceableComponent("views.voip.VideoFeed")
export default class VideoFeed extends React.Component<IProps, IState> {
export default class VideoFeed extends React.PureComponent<IProps, IState> {
private element: HTMLVideoElement;
constructor(props: IProps) {
@ -68,8 +69,15 @@ export default class VideoFeed extends React.Component<IProps, IState> {
this.updateFeed(this.props.feed, null);
}
componentDidUpdate(prevProps: IProps) {
componentDidUpdate(prevProps: IProps, prevState: IState) {
this.updateFeed(prevProps.feed, this.props.feed);
// If the mutes state has changed, we try to playMedia()
if (
prevState.videoMuted !== this.state.videoMuted ||
prevProps.feed.stream !== this.props.feed.stream
) {
this.playMedia();
}
}
static getDerivedStateFromProps(props: IProps) {
@ -94,10 +102,12 @@ export default class VideoFeed extends React.Component<IProps, IState> {
if (oldFeed) {
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
this.props.feed.removeListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
this.stopMedia();
}
if (newFeed) {
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
this.props.feed.addListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
this.playMedia();
}
}
@ -143,7 +153,13 @@ export default class VideoFeed extends React.Component<IProps, IState> {
audioMuted: this.props.feed.isAudioMuted(),
videoMuted: this.props.feed.isVideoMuted(),
});
this.playMedia();
};
private onMuteStateChanged = () => {
this.setState({
audioMuted: this.props.feed.isAudioMuted(),
videoMuted: this.props.feed.isVideoMuted(),
});
};
private onResize = (e) => {
@ -153,39 +169,58 @@ export default class VideoFeed extends React.Component<IProps, IState> {
};
render() {
const videoClasses = {
mx_VideoFeed: true,
const { pipMode, primary, feed } = this.props;
const wrapperClasses = classnames("mx_VideoFeed", {
mx_VideoFeed_voice: this.state.videoMuted,
mx_VideoFeed_video: !this.state.videoMuted,
mx_VideoFeed_mirror: (
this.props.feed.isLocal() &&
SettingsStore.getValue('VideoView.flipVideoHorizontally')
),
};
});
const micIconClasses = classnames("mx_VideoFeed_mic", {
mx_VideoFeed_mic_muted: this.state.audioMuted,
mx_VideoFeed_mic_unmuted: !this.state.audioMuted,
});
const { pipMode, primary } = this.props;
let micIcon;
if (feed.purpose !== SDPStreamMetadataPurpose.Screenshare && !pipMode) {
micIcon = (
<div className={micIconClasses} />
);
}
let content;
if (this.state.videoMuted) {
const member = this.props.feed.getMember();
let avatarSize;
if (pipMode && primary) avatarSize = 76;
else if (pipMode && !primary) avatarSize = 16;
else if (!pipMode && primary) avatarSize = 160;
else; // TBD
return (
<div className={classnames(videoClasses)}>
<MemberAvatar
member={member}
height={avatarSize}
width={avatarSize}
/>
</div>
content =(
<MemberAvatar
member={member}
height={avatarSize}
width={avatarSize}
/>
);
} else {
return (
<video className={classnames(videoClasses)} ref={this.setElementRef} />
const videoClasses = classnames("mx_VideoFeed_video", {
mx_VideoFeed_video_mirror: (
this.props.feed.isLocal() &&
SettingsStore.getValue('VideoView.flipVideoHorizontally')
),
});
content= (
<video className={videoClasses} ref={this.setElementRef} />
);
}
return (
<div className={wrapperClasses}>
{ micIcon }
{ content }
</div>
);
}
}