Improve VoIP UI/UX (#7048)
* Remove speaking indicator Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Remove mic icon for primary feed Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Remove caching as it doesn't seem to be neccessary Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Add call view buttons colors Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Refactor button code Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Update call view button icons Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * i18n Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Add dialpad icon Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Fix button sizing in PiP Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com> * Fix secondary voice video feed color Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
431098b56b
commit
bafeb38472
32 changed files with 247 additions and 383 deletions
|
@ -39,6 +39,31 @@ const TOOLTIP_Y_OFFSET = -24;
|
|||
|
||||
const CONTROLS_HIDE_DELAY = 2000;
|
||||
|
||||
interface IButtonProps {
|
||||
state: boolean;
|
||||
className: string;
|
||||
onLabel: string;
|
||||
offLabel: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const CallViewToggleButton: React.FC<IButtonProps> = ({ state: isOn, className, onLabel, offLabel, onClick }) => {
|
||||
const classes = classNames("mx_CallViewButtons_button", className, {
|
||||
mx_CallViewButtons_button_on: isOn,
|
||||
mx_CallViewButtons_button_off: !isOn,
|
||||
});
|
||||
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
className={classes}
|
||||
onClick={onClick}
|
||||
title={isOn ? onLabel : offLabel}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
call: MatrixCall;
|
||||
pipMode: boolean;
|
||||
|
@ -142,113 +167,10 @@ export default class CallViewButtons extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const micClasses = classNames("mx_CallViewButtons_button", {
|
||||
mx_CallViewButtons_button_micOn: !this.props.buttonsState.micMuted,
|
||||
mx_CallViewButtons_button_micOff: this.props.buttonsState.micMuted,
|
||||
});
|
||||
|
||||
const vidClasses = classNames("mx_CallViewButtons_button", {
|
||||
mx_CallViewButtons_button_vidOn: !this.props.buttonsState.vidMuted,
|
||||
mx_CallViewButtons_button_vidOff: this.props.buttonsState.vidMuted,
|
||||
});
|
||||
|
||||
const screensharingClasses = classNames("mx_CallViewButtons_button", {
|
||||
mx_CallViewButtons_button_screensharingOn: this.props.buttonsState.screensharing,
|
||||
mx_CallViewButtons_button_screensharingOff: !this.props.buttonsState.screensharing,
|
||||
});
|
||||
|
||||
const sidebarButtonClasses = classNames("mx_CallViewButtons_button", {
|
||||
mx_CallViewButtons_button_sidebarOn: this.props.buttonsState.sidebarShown,
|
||||
mx_CallViewButtons_button_sidebarOff: !this.props.buttonsState.sidebarShown,
|
||||
});
|
||||
|
||||
// Put the other states of the mic/video icons in the document to make sure they're cached
|
||||
// (otherwise the icon disappears briefly when toggled)
|
||||
const micCacheClasses = classNames("mx_CallViewButtons_button", "mx_CallViewButtons_button_invisible", {
|
||||
mx_CallViewButtons_button_micOn: this.props.buttonsState.micMuted,
|
||||
mx_CallViewButtons_button_micOff: !this.props.buttonsState.micMuted,
|
||||
});
|
||||
|
||||
const vidCacheClasses = classNames("mx_CallViewButtons_button", "mx_CallViewButtons_button_invisible", {
|
||||
mx_CallViewButtons_button_vidOn: this.props.buttonsState.micMuted,
|
||||
mx_CallViewButtons_button_vidOff: !this.props.buttonsState.micMuted,
|
||||
});
|
||||
|
||||
const callControlsClasses = classNames("mx_CallViewButtons", {
|
||||
mx_CallViewButtons_hidden: !this.state.visible,
|
||||
});
|
||||
|
||||
let vidMuteButton;
|
||||
if (this.props.buttonsVisibility.vidMute) {
|
||||
vidMuteButton = (
|
||||
<AccessibleTooltipButton
|
||||
className={vidClasses}
|
||||
onClick={this.props.handlers.onVidMuteClick}
|
||||
title={this.props.buttonsState.vidMuted ? _t("Start the camera") : _t("Stop the camera")}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let screensharingButton;
|
||||
if (this.props.buttonsVisibility.screensharing) {
|
||||
screensharingButton = (
|
||||
<AccessibleTooltipButton
|
||||
className={screensharingClasses}
|
||||
onClick={this.props.handlers.onScreenshareClick}
|
||||
title={this.props.buttonsState.screensharing
|
||||
? _t("Stop sharing your screen")
|
||||
: _t("Start sharing your screen")
|
||||
}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let sidebarButton;
|
||||
if (this.props.buttonsVisibility.sidebar) {
|
||||
sidebarButton = (
|
||||
<AccessibleTooltipButton
|
||||
className={sidebarButtonClasses}
|
||||
onClick={this.props.handlers.onToggleSidebarClick}
|
||||
title={this.props.buttonsState.sidebarShown ? _t("Hide sidebar") : _t("Show sidebar")}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let contextMenuButton;
|
||||
if (this.props.buttonsVisibility.contextMenu) {
|
||||
contextMenuButton = (
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_CallViewButtons_button mx_CallViewButtons_button_more"
|
||||
onClick={this.onMoreClick}
|
||||
inputRef={this.contextMenuButton}
|
||||
isExpanded={this.state.showMoreMenu}
|
||||
title={_t("More")}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/>
|
||||
);
|
||||
}
|
||||
let dialpadButton;
|
||||
if (this.props.buttonsVisibility.dialpad) {
|
||||
dialpadButton = (
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_CallViewButtons_button mx_CallViewButtons_dialpad"
|
||||
inputRef={this.dialpadButton}
|
||||
onClick={this.onDialpadClick}
|
||||
isExpanded={this.state.showDialpad}
|
||||
title={_t("Dialpad")}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let dialPad;
|
||||
if (this.state.showDialpad) {
|
||||
dialPad = <DialpadContextMenu
|
||||
|
@ -289,20 +211,53 @@ export default class CallViewButtons extends React.Component<IProps, IState> {
|
|||
>
|
||||
{ dialPad }
|
||||
{ contextMenu }
|
||||
{ dialpadButton }
|
||||
<AccessibleTooltipButton
|
||||
className={micClasses}
|
||||
onClick={this.props.handlers.onMicMuteClick}
|
||||
title={this.props.buttonsState.micMuted ? _t("Unmute the microphone") : _t("Mute the microphone")}
|
||||
|
||||
{ this.props.buttonsVisibility.dialpad && <ContextMenuTooltipButton
|
||||
className="mx_CallViewButtons_button mx_CallViewButtons_dialpad"
|
||||
inputRef={this.dialpadButton}
|
||||
onClick={this.onDialpadClick}
|
||||
isExpanded={this.state.showDialpad}
|
||||
title={_t("Dialpad")}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/> }
|
||||
<CallViewToggleButton
|
||||
state={!this.props.buttonsState.micMuted}
|
||||
className="mx_CallViewButtons_button_mic"
|
||||
onLabel={_t("Mute the microphone")}
|
||||
offLabel={_t("Unmute the microphone")}
|
||||
onClick={this.props.handlers.onMicMuteClick}
|
||||
/>
|
||||
{ vidMuteButton }
|
||||
<div className={micCacheClasses} />
|
||||
<div className={vidCacheClasses} />
|
||||
{ screensharingButton }
|
||||
{ sidebarButton }
|
||||
{ contextMenuButton }
|
||||
{ this.props.buttonsVisibility.vidMute && <CallViewToggleButton
|
||||
state={!this.props.buttonsState.vidMuted}
|
||||
className="mx_CallViewButtons_button_vid"
|
||||
onLabel={_t("Stop the camera")}
|
||||
offLabel={_t("Start the camera")}
|
||||
onClick={this.props.handlers.onVidMuteClick}
|
||||
/> }
|
||||
{ this.props.buttonsVisibility.screensharing && <CallViewToggleButton
|
||||
state={this.props.buttonsState.screensharing}
|
||||
className="mx_CallViewButtons_button_screensharing"
|
||||
onLabel={_t("Stop sharing your screen")}
|
||||
offLabel={_t("Start sharing your screen")}
|
||||
onClick={this.props.handlers.onScreenshareClick}
|
||||
/> }
|
||||
{ this.props.buttonsVisibility.sidebar && <CallViewToggleButton
|
||||
state={this.props.buttonsState.sidebarShown}
|
||||
className="mx_CallViewButtons_button_sidebar"
|
||||
onLabel={_t("Hide sidebar")}
|
||||
offLabel={_t("Show sidebar")}
|
||||
onClick={this.props.handlers.onToggleSidebarClick}
|
||||
/> }
|
||||
{ this.props.buttonsVisibility.contextMenu && <ContextMenuTooltipButton
|
||||
className="mx_CallViewButtons_button mx_CallViewButtons_button_more"
|
||||
onClick={this.onMoreClick}
|
||||
inputRef={this.contextMenuButton}
|
||||
isExpanded={this.state.showMoreMenu}
|
||||
title={_t("More")}
|
||||
alignment={Alignment.Top}
|
||||
yOffset={TOOLTIP_Y_OFFSET}
|
||||
/> }
|
||||
<AccessibleTooltipButton
|
||||
className="mx_CallViewButtons_button mx_CallViewButtons_button_hangup"
|
||||
onClick={this.props.handlers.onHangupClick}
|
||||
|
|
|
@ -45,7 +45,6 @@ interface IProps {
|
|||
interface IState {
|
||||
audioMuted: boolean;
|
||||
videoMuted: boolean;
|
||||
speaking: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.voip.VideoFeed")
|
||||
|
@ -58,7 +57,6 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
this.state = {
|
||||
audioMuted: this.props.feed.isAudioMuted(),
|
||||
videoMuted: this.props.feed.isVideoMuted(),
|
||||
speaking: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -106,7 +104,6 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||
this.props.feed.removeListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
||||
if (this.props.feed.purpose === SDPStreamMetadataPurpose.Usermedia) {
|
||||
this.props.feed.removeListener(CallFeedEvent.Speaking, this.onSpeaking);
|
||||
this.props.feed.measureVolumeActivity(false);
|
||||
}
|
||||
this.stopMedia();
|
||||
|
@ -115,7 +112,6 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||
this.props.feed.addListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
||||
if (this.props.feed.purpose === SDPStreamMetadataPurpose.Usermedia) {
|
||||
this.props.feed.addListener(CallFeedEvent.Speaking, this.onSpeaking);
|
||||
this.props.feed.measureVolumeActivity(true);
|
||||
}
|
||||
this.playMedia();
|
||||
|
@ -172,10 +168,6 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
private onSpeaking = (speaking: boolean): void => {
|
||||
this.setState({ speaking });
|
||||
};
|
||||
|
||||
private onResize = (e) => {
|
||||
if (this.props.onResize && !this.props.feed.isLocal()) {
|
||||
this.props.onResize(e);
|
||||
|
@ -187,7 +179,6 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
|
||||
const wrapperClasses = classnames("mx_VideoFeed", {
|
||||
mx_VideoFeed_voice: this.state.videoMuted,
|
||||
mx_VideoFeed_speaking: this.state.speaking,
|
||||
});
|
||||
const micIconClasses = classnames("mx_VideoFeed_mic", {
|
||||
mx_VideoFeed_mic_muted: this.state.audioMuted,
|
||||
|
@ -195,7 +186,11 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
});
|
||||
|
||||
let micIcon;
|
||||
if (feed.purpose !== SDPStreamMetadataPurpose.Screenshare && !pipMode) {
|
||||
if (
|
||||
feed.purpose !== SDPStreamMetadataPurpose.Screenshare &&
|
||||
!primary &&
|
||||
!pipMode
|
||||
) {
|
||||
micIcon = (
|
||||
<div className={micIconClasses} />
|
||||
);
|
||||
|
|
|
@ -929,16 +929,16 @@
|
|||
"%(sharerName)s is presenting": "%(sharerName)s is presenting",
|
||||
"Your camera is turned off": "Your camera is turned off",
|
||||
"Your camera is still enabled": "Your camera is still enabled",
|
||||
"Start the camera": "Start the camera",
|
||||
"Dialpad": "Dialpad",
|
||||
"Mute the microphone": "Mute the microphone",
|
||||
"Unmute the microphone": "Unmute the microphone",
|
||||
"Stop the camera": "Stop the camera",
|
||||
"Start the camera": "Start the camera",
|
||||
"Stop sharing your screen": "Stop sharing your screen",
|
||||
"Start sharing your screen": "Start sharing your screen",
|
||||
"Hide sidebar": "Hide sidebar",
|
||||
"Show sidebar": "Show sidebar",
|
||||
"More": "More",
|
||||
"Dialpad": "Dialpad",
|
||||
"Unmute the microphone": "Unmute the microphone",
|
||||
"Mute the microphone": "Mute the microphone",
|
||||
"Hangup": "Hangup",
|
||||
"Video Call": "Video Call",
|
||||
"Voice Call": "Voice Call",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue