Merge pull request #5693 from matrix-org/travis/move-call-buttons

Move call buttons to the room header
pull/21833/head
Travis Ralston 2021-02-26 13:56:44 -07:00 committed by GitHub
commit cf0f591cba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 144 deletions

View File

@ -227,18 +227,6 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/room/composer/attach.svg');
}
.mx_MessageComposer_hangup::before {
mask-image: url('$(res)/img/element-icons/call/hangup.svg');
}
.mx_MessageComposer_voicecall::before {
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
}
.mx_MessageComposer_videocall::before {
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
}
.mx_MessageComposer_emoji::before {
mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg');
}

View File

@ -252,6 +252,19 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/room/search-inset.svg');
}
.mx_RoomHeader_voiceCallButton::before {
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
// The call button SVG is padded slightly differently, so match it up to the size
// of the other icons
mask-size: 20px;
mask-position: center;
}
.mx_RoomHeader_videoCallButton::before {
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
}
.mx_RoomHeader_showPanel {
height: 16px;
}

View File

@ -34,7 +34,7 @@ import ResizeNotifier from '../../utils/ResizeNotifier';
import ContentMessages from '../../ContentMessages';
import Modal from '../../Modal';
import * as sdk from '../../index';
import CallHandler from '../../CallHandler';
import CallHandler, { PlaceCallType } from '../../CallHandler';
import dis from '../../dispatcher/dispatcher';
import Tinter from '../../Tinter';
import rateLimitedFunc from '../../ratelimitedfunc';
@ -1352,6 +1352,14 @@ export default class RoomView extends React.Component<IProps, IState> {
SettingsStore.setValue("PinnedEvents.isOpen", roomId, SettingLevel.ROOM_DEVICE, nowShowingPinned);
};
private onCallPlaced = (type: PlaceCallType) => {
dis.dispatch({
action: 'place_call',
type: type,
room_id: this.state.room.roomId,
});
};
private onSettingsClick = () => {
dis.dispatch({ action: "open_room_settings" });
};
@ -2031,6 +2039,7 @@ export default class RoomView extends React.Component<IProps, IState> {
e2eStatus={this.state.e2eStatus}
onAppsClick={this.state.hasPinnedWidgets ? this.onAppsClick : null}
appsShown={this.state.showApps}
onCallPlaced={this.onCallPlaced}
/>
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
<div className="mx_RoomView_body">

View File

@ -1,7 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017, 2018 New Vector Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.
Copyright 2015-2018, 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -19,7 +17,6 @@ import React, {createRef} from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import CallHandler from '../../../CallHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import dis from '../../../dispatcher/dispatcher';
@ -33,11 +30,8 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import ReplyPreview from "./ReplyPreview";
import {UIFeature} from "../../../settings/UIFeature";
import WidgetStore from "../../../stores/WidgetStore";
import WidgetUtils from "../../../utils/WidgetUtils";
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
import ActiveWidgetStore from "../../../stores/ActiveWidgetStore";
import { PlaceCallType } from "../../../CallHandler";
import { CallState } from 'matrix-js-sdk/src/webrtc/call';
function ComposerAvatar(props) {
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
@ -50,97 +44,6 @@ ComposerAvatar.propTypes = {
me: PropTypes.object.isRequired,
};
function CallButton(props) {
const onVoiceCallClick = (ev) => {
dis.dispatch({
action: 'place_call',
type: PlaceCallType.Voice,
room_id: props.roomId,
});
};
return (<AccessibleTooltipButton
className="mx_MessageComposer_button mx_MessageComposer_voicecall"
onClick={onVoiceCallClick}
title={_t('Voice call')}
/>);
}
CallButton.propTypes = {
roomId: PropTypes.string.isRequired,
};
function VideoCallButton(props) {
const onCallClick = (ev) => {
dis.dispatch({
action: 'place_call',
type: ev.shiftKey ? PlaceCallType.ScreenSharing : PlaceCallType.Video,
room_id: props.roomId,
});
};
return <AccessibleTooltipButton
className="mx_MessageComposer_button mx_MessageComposer_videocall"
onClick={onCallClick}
title={_t('Video call')}
/>;
}
VideoCallButton.propTypes = {
roomId: PropTypes.string.isRequired,
};
function HangupButton(props) {
const onHangupClick = () => {
if (props.isConference) {
dis.dispatch({
action: props.canEndConference ? 'end_conference' : 'hangup_conference',
room_id: props.roomId,
});
return;
}
const call = CallHandler.sharedInstance().getCallForRoom(props.roomId);
if (!call) {
return;
}
const action = call.state === CallState.Ringing ? 'reject' : 'hangup';
dis.dispatch({
action,
// hangup the call for this room. NB. We use the room in props as the room ID
// as call.roomId may be the 'virtual room', and the dispatch actions always
// use the user-facing room (there was a time when we deliberately used
// call.roomId and *not* props.roomId, but that was for the old
// style Freeswitch conference calls and those times are gone.)
room_id: props.roomId,
});
};
let tooltip = _t("Hangup");
if (props.isConference && props.canEndConference) {
tooltip = _t("End conference");
}
const canLeaveConference = !props.isConference ? true : props.isInConference;
return (
<AccessibleTooltipButton
className="mx_MessageComposer_button mx_MessageComposer_hangup"
onClick={onHangupClick}
title={tooltip}
disabled={!canLeaveConference}
/>
);
}
HangupButton.propTypes = {
roomId: PropTypes.string.isRequired,
isConference: PropTypes.bool.isRequired,
canEndConference: PropTypes.bool,
isInConference: PropTypes.bool,
};
const EmojiButton = ({addEmoji}) => {
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
@ -265,7 +168,6 @@ export default class MessageComposer extends React.Component {
this.state = {
tombstone: this._getRoomTombstone(),
canSendMessages: this.props.room.maySendMessage(),
showCallButtons: SettingsStore.getValue("showCallButtonsInComposer"),
hasConference: WidgetStore.instance.doesRoomHaveConference(this.props.room),
joinedConference: WidgetStore.instance.isJoinedToConferenceIn(this.props.room),
};
@ -405,12 +307,7 @@ export default class MessageComposer extends React.Component {
];
if (!this.state.tombstone && this.state.canSendMessages) {
// This also currently includes the call buttons. Really we should
// check separately for whether we can call, but this is slightly
// complex because of conference calls.
const SendMessageComposer = sdk.getComponent("rooms.SendMessageComposer");
const callInProgress = this.props.callState && this.props.callState !== 'ended';
controls.push(
<SendMessageComposer
@ -430,30 +327,6 @@ export default class MessageComposer extends React.Component {
SettingsStore.getValue("MessageComposerInput.showStickersButton")) {
controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />);
}
if (this.state.showCallButtons) {
if (this.state.hasConference) {
const canEndConf = WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
controls.push(
<HangupButton
key="controls_hangup"
roomId={this.props.room.roomId}
isConference={true}
canEndConference={canEndConf}
isInConference={this.state.joinedConference}
/>,
);
} else if (callInProgress) {
controls.push(
<HangupButton key="controls_hangup" roomId={this.props.room.roomId} isConference={false} />,
);
} else {
controls.push(
<CallButton key="controls_call" roomId={this.props.room.roomId} />,
<VideoCallButton key="controls_videocall" roomId={this.props.room.roomId} />,
);
}
}
} else if (this.state.tombstone) {
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];

View File

@ -31,6 +31,7 @@ import {DefaultTagID} from "../../../stores/room-list/models";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import RoomTopic from "../elements/RoomTopic";
import RoomName from "../elements/RoomName";
import {PlaceCallType} from "../../../CallHandler";
export default class RoomHeader extends React.Component {
static propTypes = {
@ -45,6 +46,7 @@ export default class RoomHeader extends React.Component {
e2eStatus: PropTypes.string,
onAppsClick: PropTypes.func,
appsShown: PropTypes.bool,
onCallPlaced: PropTypes.func, // (PlaceCallType) => void;
};
static defaultProps = {
@ -226,8 +228,26 @@ export default class RoomHeader extends React.Component {
title={_t("Search")} />;
}
let voiceCallButton;
let videoCallButton;
if (this.props.inRoom && SettingsStore.getValue("showCallButtonsInComposer")) {
voiceCallButton =
<AccessibleTooltipButton
className="mx_RoomHeader_button mx_RoomHeader_voiceCallButton"
onClick={() => this.props.onCallPlaced(PlaceCallType.Voice)}
title={_t("Voice call")} />;
videoCallButton =
<AccessibleTooltipButton
className="mx_RoomHeader_button mx_RoomHeader_videoCallButton"
onClick={(ev) => this.props.onCallPlaced(
ev.shiftKey ? PlaceCallType.ScreenSharing : PlaceCallType.Video)}
title={_t("Video call")} />;
}
const rightRow =
<div className="mx_RoomHeader_buttons">
{ videoCallButton }
{ voiceCallButton }
{ pinnedEventsButton }
{ forgetButton }
{ appsButton }

View File

@ -1416,9 +1416,6 @@
"Invited": "Invited",
"Filter room members": "Filter room members",
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)",
"Voice call": "Voice call",
"Video call": "Video call",
"Hangup": "Hangup",
"Emoji picker": "Emoji picker",
"Upload file": "Upload file",
"Send an encrypted reply…": "Send an encrypted reply…",
@ -1476,6 +1473,8 @@
"Hide Widgets": "Hide Widgets",
"Show Widgets": "Show Widgets",
"Search": "Search",
"Voice call": "Voice call",
"Video call": "Video call",
"Start a Conversation": "Start a Conversation",
"Open dial pad": "Open dial pad",
"Invites": "Invites",

View File

@ -632,6 +632,8 @@ export const SETTINGS: {[setting: string]: ISetting} = {
default: 3000,
},
"showCallButtonsInComposer": {
// Dev note: This is no longer "in composer" but is instead "in room header".
// TODO: Rename with settings v3
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
default: true,
controller: new UIFeatureController(UIFeature.Voip),